一、动态SQL
动态 SQL 是 MyBatis 的强大特性之一,可以根据不同条件拼接 SQL 语句。最好先写出完整的SQL语句,在按照动态SQL的标签规则等去进行排列组合。
-
if
List<Blog> queryBlogIf(Map map);
<select id="queryBlogIf" parameterType="map" resultType="blog"> select * from mybatis.Blog <where> <if test="title != null"> and title = #{title} </if> <if test="author != null"> and author=#{author} </if> </where> </select>
@Test public void queryBlogIf() { SqlSession sqlSession = MybatisUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap hashMap = new HashMap(); // hashMap.put("title", "spring"); hashMap.put("author", "olarian"); List<Blog> blogs = mapper.queryBlogIf(hashMap); for (Blog blog : blogs) { System.out.println(blog); } sqlSession.close(); }
快捷实现了SQL的复用,避免繁琐的方法重载
-
choose (when, otherwise)
把这看做switch-case-default
List<Blog> queryBlogChoose(Map map);
<select id="queryBlogChoose" parameterType="map" resultType="blog"> select * from mybatis.blog <where> <choose> <when test="title!=null"> title = #{title} </when> <when test="author!=null"> author = #{author} </when> <otherwise> views=443 </otherwise> </choose> </where> </select>
@Test public void queryBlogChoose() { SqlSession sqlSession = MybatisUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap hashMap = new HashMap(); hashMap.put("title", "mybatis"); hashMap.put("author", "olarian"); hashMap.put("views", 443); List<Blog> blogs = mapper.queryBlogChoose(hashMap); for (Blog blog : blogs) { System.out.println(blog); } sqlSession.close(); }
-
trim (where, set)
-
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
-
set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。
void updateBlog(Map map);
<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>
@Test public void updateBlog() { SqlSession sqlSession = MybatisUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap hashMap = new HashMap(); hashMap.put("title", "mybatis-plus"); hashMap.put("author", "olarian"); hashMap.put("id", "eea97146be2f46b3a5d4b5a2fd14336c"); mapper.updateBlog(hashMap); sqlSession.close(); }
-
trim可以设置前后缀并进行覆盖
<trim prefix="" prefixOverrides="" suffix="" suffixOverrides=""> </trim>
where和set本质上是trim的特殊形式:
<!-- where --> <trim prefix="WHERE" prefixOverrides=" AND | OR "> ... </trim> <!-- set --> <trim prefix="SET" suffixOverrides=","> ... </trim>
-
-
foreach
动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)
List<Blog> queryBlogForeach(Map map);
<select id="queryBlogForeach" parameterType="map" resultType="blog"> select * from mybatis.blog <where> id in <foreach collection="ids" separator="," open="(" close=")" item="id"> #{id} </foreach> </where> </select>
@Test public void queryBlogForeach() { SqlSession sqlSession = MybatisUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap hashMap = new HashMap(); ArrayList<Integer> ids = new ArrayList<>(); hashMap.put("ids", ids); ids.add(1); ids.add(2); List<Blog> blogs = mapper.queryBlogForeach(hashMap); for (Blog blog : blogs) { System.out.println(blog); } sqlSession.close(); }
-
SQL片段
设置常用SQL,便于复用
<sql id="sql01"> <if test="title != null"> and title = #{title} </if> <if test="author != null"> and author=#{author} </if> </sql>
再使用<include refid=“sql01”>标签引用就行了!
注意事项:
- 最好基于单表来定义SQL片段
- SQL片段中不要存在where标签
二、缓存
Q:什么是缓存?
A:缓存(Cache)本意是指可以进行高速数据交换的存储器,通俗点来说,就是通过将数据提前存放到内存,以提高访问速度。
Q:什么时候使用缓存?
A:对于那些经常需要查询并且不经常改变的数据,可以使用缓存来增加读取速度。
Mybatis缓存机制:
-
一级缓存
默认情况下,只有一级缓存开启(SqlSession级别的缓存,也成为本地缓存),sqlsession.close()后缓存被清楚!
@Test public void queryUserById() { SqlSession sqlSession = MybatisUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap hashMap = new HashMap(); hashMap.put("id", "1"); Blog blog1 = mapper.queryBlogById(hashMap); Blog blog2 = mapper.queryBlogById(hashMap); System.out.println(blog1); System.out.println(blog2); System.out.println(blog1 == blog2); sqlSession.close(); } // 在一个Session中查询两次相同记录,通过查看日志发现,只进行了一次查询,并且两次查询结果为true
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 1613095350. [com.ola.dao.BlogMapper.queryBlogById]-==> Preparing: select * from mybatis.blog where id = ?; [com.ola.dao.BlogMapper.queryBlogById]-==> Parameters: 1(String) [com.ola.dao.BlogMapper.queryBlogById]-<== Total: 1 ============================================= Blog(id=1, title=spring, author=olarian, createTime=Wed Jan 13 12:45:11 CST 2021, views=443) Blog(id=1, title=spring, author=olarian, createTime=Wed Jan 13 12:45:11 CST 2021, views=443) true [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@6025e1b6] [org.apache.ibatis.datasource.pooled.PooledDataSource]-Returned connection 1613095350 to pool. 进程已结束,退出代码0
Q:什么时候缓存会失效?
A:
-
查询不同记录
-
增删改操作(可能会改变原来数据的操作)
-
查询不同的Mapper
-
手动清理缓存
sqlSession.clearCache();
-
-
二级缓存(只要mapper还在,二级缓存就在,提升了缓存作用域)
-
只有当一级缓存会话提交或者关闭的时候,二级缓存才会生效
-
二级缓存需要手动开启和配置,是基于namespace级别的缓存(一个Mapper一个二级缓存)
开启方式:
-
在mybatis-config.xml中的settings标签下显式配置开启全局缓存(默认也是开启的,但是最好显式的配置一下)
<settings> <setting name="cacheEnabled" value="true"/> </settings>
-
在你的 SQL 映射文件中添加一行:
<cache/> <!-- 这些属性可以通过 cache 元素的属性来修改。比如: <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/> 这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的 对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。 --> <!-- 可用的清除策略有: LRU – 最近最少使用:移除最长时间不被使用的对象。 FIFO – 先进先出:按对象进入缓存的顺序来移除它们。 SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。 WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。 默认的清除策略是 LRU。 --> <!-- flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。 size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。 readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。 --> <!-- 注意:二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新。 -->
-
-
缓存原理
- 自定义缓存ehcache
三、其他
-
以下给出一个从 mybatis-config.xml 文件创建 SqlSessionFactory 的示例:
String resource = "org/mybatis/builder/mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(inputStream);
完整工具类:
package com.ola.utils; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; /** * ClassName:MybatisUtils Package:com.ola.utils * * @author morningj * @date 2021/1/10 15:39 */ public class MybatisUtils { private static SqlSessionFactory sqlSessionFactory; static { try { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } public static SqlSession getSqlSession() { return sqlSessionFactory.openSession(true); } }
-
注册mapper最好使用class注册,但是要注意类和xml配置要在同一个包下