Mybatis学习四: 一、动态sql 1、什么是动态sql: 动态sql是指根据不同的查询条件,生成不同的sql语句。
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。
如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。
我们之前写的 SQL 语句都比较简单,如果有比较复杂的业务,我们需要写复杂的 SQL 语句,往往需要拼接,而拼接 SQL ,稍微不注意,由于引号,空格等缺失可能都会导致错误。
那么怎么去解决这个问题呢?这就要使用 mybatis 动态SQL,通过 if, choose, when, otherwise,trim, where, set, foreach等标签,可组合成非常灵活的SQL语句,从而在提高 SQL 语句的准确性的同时,也大大提高了开发人员的效率。
2、新建数据库表 新建一个数据库表blog:
1 2 3 4 5 6 7 8 CREATE TABLE `blog`(`id` VARCHAR (50 ) NOT NULL COMMENT '博客id' , `title` VARCHAR (100 ) NOT NULL COMMENT '博客标题' , `author` VARCHAR (30 ) NOT NULL COMMENT '博客作者' , `create_time` DATETIME NOT NULL COMMENT '创建时间' , `views` INT (30 ) NOT NULL COMMENT '浏览量' )ENGINE= INNODB DEFAULT CHARSET= utf8 SELECT * FROM mybatis.blog WHERE 1 = 1
3、创建idutil工具类: 1 2 3 4 5 6 7 8 9 10 11 12 13 import org.junit.jupiter.api.Test;import java.util.UUID;public class IDUtils { public static String getID () { return UUID.randomUUID().toString().replaceAll("-" , "" ); } @Test public void test () { System.out.println(IDUtils.getID()); } }
4、编写blog实体类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 import java.util.Date;public class Blog { private String id; private String title; private String author; private Date createTime; private int views; public Blog () { } @Override public String toString () { return "Blog{" + "id='" + id + '\'' + ", title='" + title + '\'' + ", author='" + author + '\'' + ", createTime=" + createTime + ", views=" + views + '}' ; } public Blog (String id, String title, String author, Date createTime, int views) { this .id = id; this .title = title; this .author = author; this .createTime = createTime; this .views = views; } public String getId () { return id; } public void setId (String id) { this .id = id; } public String getTitle () { return title; } public void setTitle (String title) { this .title = title; } public String getAuthor () { return author; } public void setAuthor (String author) { this .author = author; } public Date getCreateTime () { return createTime; } public void setCreateTime (Date createTime) { this .createTime = createTime; } public int getViews () { return views; } public void setViews (int views) { this .views = views; } }
5、编写mybatis核心配置文件: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd" > <configuration > <settings > <setting name ="logImpl" value ="LOG4J" /> <setting name ="mapUnderscoreToCamelCase" value ="true" /> </settings > <typeAliases > <typeAlias type ="pojo.Blog" alias ="blog" > </typeAlias > </typeAliases > <environments default ="development" > <environment id ="development" > <transactionManager type ="JDBC" /> <dataSource type ="POOLED" > <property name ="driver" value ="com.mysql.cj.jdbc.Driver" /> <property name ="url" value ="jdbc:mysql://localhost:3306/mybatis?useSSL=false& useUnicode=true& characterEncoding=utf-8" /> <property name ="username" value ="root" /> <property name ="password" value ="root" /> </dataSource > </environment > </environments > <mappers > <mapper class ="dao.BlogMapper" > </mapper > </mappers > </configuration >
6、插入初始数据 在BlogMapper里写上添加方法
mapper配置文件中:
1 2 3 4 5 6 7 8 9 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="dao.BlogMapper" > <insert id ="addBlog" parameterType ="pojo.Blog" > insert into mybatis.blog(`id`,`title`,`author`,`create_time`,`views`)values (#{id},#{title},#{author},#{createTime},#{views}) </insert > </mapper >
初始化博客:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 import dao.BlogMapper;import org.apache.ibatis.session.SqlSession;import org.junit.Test;import pojo.Blog;import utils.IDUtils;import utils.MybatisUtils;import java.util.Date;import java.util.HashMap;import java.util.List;public class MyTest { @Test public void addInitBlog () { SqlSession session = MybatisUtils.getSqlSession(); BlogMapper mapper = session.getMapper(BlogMapper.class); Blog blog = new Blog(); blog.setId(IDUtils.getID()); blog.setTitle("Mybatis如此简单" ); blog.setAuthor("lsh" ); blog.setCreateTime(new Date()); blog.setViews(9999 ); mapper.addBlog(blog); blog.setId(IDUtils.getID()); blog.setTitle("Java如此简单" ); mapper.addBlog(blog); blog.setId(IDUtils.getID()); blog.setTitle("Spring如此简单" ); mapper.addBlog(blog); blog.setId(IDUtils.getID()); blog.setTitle("微服务如此简单" ); mapper.addBlog(blog); session.commit(); session.close(); } ### 二、动态sql中的if语句 #### 1、编写接口类 ```java List<Blog> queryBlogIF (Map map) ;
2、编写sql语句 1 2 3 4 5 6 7 8 9 <select id ="queryBlogIf" parameterType ="map" resultType ="blog" > select * from blog where <if test ="title != null" > title = #{title} </if > <if test ="author != null" > and author = #{author} </if > </select >
3、测试类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Test public void queryBlogIF () { SqlSession sqlSession = MybatisUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap map = new HashMap(); map.put("title" ,"Java如此简单2" ); map.put("author" ,"lsh" ); List<Blog> blogs = mapper.queryBlogIF(map); for (Blog blog : blogs) { System.out.println(blog); } sqlSession.commit(); sqlSession.close(); }
这样写我们可以看到,如果 author 等于 null,那么查询语句为 select * from user where title=#{title},
但是如果title为空呢?那么查询语句为 select * from user where and author=#{author},这是错误的
SQL 语句,如何解决呢?请看下面的 where 语句!
三、动态sql中的where语句 修改上面的SQL语句;
1 2 3 4 5 6 7 8 9 10 11 <select id ="queryBlogIf" parameterType ="map" resultType ="blog" > select * from blog <where > <if test ="title != null" > title = #{title} </if > <if test ="author != null" > and author = #{author} </if > </where > </select >
这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个‘where’。此外,如果标签返
回的内容是以AND 或OR 开头的,则它会剔除掉。【这是我们使用的最多的案例】
四、动态sql中的set语句 1、编写接口方法 1 int updateBlog (Map map) ;
2、sql配置文件 1 2 3 4 5 6 7 8 9 10 11 12 <update id ="updateBlog" parameterType ="map" > update mybatis.blog <set > <if test ="title!=null" > title = #{title}, </if > <if test ="author!=null" > author = #{author} </if > </set > where id = #{id} </update >
3、测试 1 2 3 4 5 6 7 8 9 10 @Test public void testUpdateBlog () { SqlSession session = MybatisUtils.getSession(); BlogMapper mapper = session.getMapper(BlogMapper.class); HashMap<String, String> map = new HashMap<String, String>(); map.put("title" ,"动态SQL" ); map.put("author" ,"lsh2" ); map.put("id" ,"32ee95f5bb35428da6220065e248be11" ); mapper.updateBlog(map); session.close(); }
五、动态sql中的choose语句 1、编写接口方法 1 List<Blog> queryBlogChoose (Map map) ;
2、sql配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <select id ="queryBlogChoose" parameterType ="map" resultType ="blog" > select * from mybatis.blog <where > <choose > <when test ="title!=null" > title=#{title} </when > <when test ="author!=null" > and author=#{author} </when > <otherwise > and views=#{views} </otherwise > </choose > </where > </select >
3、测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Test public void queryBlogChoose () { SqlSession sqlSession = MybatisUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap map = new HashMap(); map.put("title" ,"Java如此简单" ); map.put("views" ,9999 ); List<Blog> blogs = mapper.queryBlogChoose(map); for (Blog blog : blogs) { System.out.println(blog); } }
六、sql片段 有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽
取出来,然后使用时直接调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <sql id ="if-title-author" > <if test ="title!=null" > and title = #{title} </if > <if test ="author!=null" > and author = #{author} </if > </sql > <select id ="queryBlogIF" parameterType ="map" resultType ="blog" > select * from mybatis.blog <where > <include refid ="if-title-author" > </include > </where > </select >
七、foreach 将数据库中前三个数据的id修改为1,2,3;
需求:我们需要查询 blog 表中 id 分别为1,2,3的博客信息
1、编写接口 1 List<Blog> queryBlogForeach (Map map) ;
2、编写sql语句 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <select id ="queryBlogForeach" parameterType ="map" resultType ="blog" > select * from blog <where > <foreach collection ="ids" item ="id" open ="and (" close =")" separator ="or" > id=#{id} </foreach > </where > </select >
3、测试 1 2 3 4 5 6 7 8 9 10 11 12 13 @Test public void testQueryBlogForeach () { SqlSession session = MybatisUtils.getSession(); BlogMapper mapper = session.getMapper(BlogMapper.class); HashMap map = new HashMap(); List<Integer> ids = new ArrayList<Integer>(); ids.add(1 ); ids.add(2 ); ids.add(3 ); map.put("ids" ,ids); List<Blog> blogs = mapper.queryBlogForeach(map); System.out.println(blogs); session.close(); }
八、mybatis缓存(简略)
、什么是缓存 [ Cache ]?
存在内存中的临时数据。
将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
为什么使用缓存?
减少和数据库的交互次数,减少系统开销,提高系统效率。
什么样的数据能使用缓存?
经常查询并且不经常改变的数据。
MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
MyBatis系统中默认定义了两级缓存:一级缓存 和二级缓存
默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
一级缓存是SqlSession级别的缓存,是一直开启的,我们关闭不了它;
一级缓存失效情况:没有使用到当前的一级缓存,效果就是,还需要再向数据库中发起一次查询请求!
二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
工作机制
一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
新的会话查询信息,就可以从二级缓存中获取内容;
不同的mapper查出的数据会放在自己对应的缓存(map)中;
只要开启了二级缓存,我们在同一个Mapper中的查询,可以在二级缓存中拿到数据
查出的数据都会被默认先放在一级缓存中
只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中
EhCache
第三方缓存实现–EhCache: 查看百度百科
官方文档
Ehcache是一种广泛使用的java分布式缓存,用于通用缓存;
要在应用程序中使用Ehcache,需要引入依赖的jar包
在mapper.xml中使用对应的缓存即可
1 2 <mapper namespace = “org.acme.FooMapper” > <cache type = “org.mybatis.caches.ehcache.EhcacheCache” /> </mapper >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation ="http://ehcache.org/ehcache.xsd" updateCheck ="false" > <diskStore path ="./tmpdir/Tmp_EhCache" /> <defaultCache eternal ="false" maxElementsInMemory ="10000" overflowToDisk ="false" diskPersistent ="false" timeToIdleSeconds ="1800" timeToLiveSeconds ="259200" memoryStoreEvictionPolicy ="LRU" /> <cache name ="cloud_user" eternal ="false" maxElementsInMemory ="5000" overflowToDisk ="false" diskPersistent ="false" timeToIdleSeconds ="1800" timeToLiveSeconds ="1800" memoryStoreEvictionPolicy ="LRU" /> </ehcache >