Mybatis中如何实现二级缓存机制?

2026-05-19 15:541阅读0评论SEO基础
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计2813个文字,预计阅读时间需要12分钟。

Mybatis中如何实现二级缓存机制?

1.2 二级缓存【官方声明】=如何开启【二级缓存】+ 默认情况下,只启用了本地的会话缓存,它仅对单个会话中的数据进行缓存。它仅对单个会话中的数据进行缓存。若要启用全局的二级缓存,只需在您的 SQL 映射文件中添加相应的配置。

1.2 二级缓存
  • => 如何开启

    默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:

    • 在XML映射文件中添加以下代码,以开启

<cache/>

  • => 的作用

    • 映射语句文件中的所有 select 语句的结果将会被缓存。
    • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
    • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
    • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
    • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
    • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
  • => 的作用域

    • 缓存只作用于 cache 标签所在的映射文件中的语句。如果你混合使用 Java API 和 XML 映射文件,在共用接口中的语句将不会被默认缓存。你需要使用 @CacheNamespaceRef 注解指定缓存作用域。
  • => <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。

  • 总结
1.2.1 什么是二级缓存?

MyBatis的二级缓存是Application级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能。

二级缓存即使当一级缓存被清除/关闭也会存在(即sqlsession.close()方法执行后依旧会保存查询缓存)

SqlSessionFactory层面上的二级缓存默认是不开启的,二级缓存的开启需要进行配置,实现二级缓存的时候,MyBatis要求返回的POJO必须是可序列化的( 要求实现Serializable接口)

1.2.2 二级缓存的作用
  • 映射语句文件中的所有select语句将会被缓存。
  • 映射语句文件中的所有insert、update和delete语句会刷新缓存。
  • 缓存会使用默认的Least Recently Used(LRU,最近最少使用的)算法来收回。
  • 根据时间表,比如No Flush Interval,(CNFI没有刷新间隔),缓存不会以任何时间顺序来刷新。
  • 缓存会存储列表集合或对象(无论查询方法返回什么)的1024个引用
  • 缓存会被视为是read/write(可读/可写)的缓存,意味着对象检索不是共享的,而且可以安全的被调用者修改,不干扰其他调用者或线程所做的潜在修改。
1.2.3 测试
  1. 实现类 =>

@Data //实现二级缓存返回的pojo对象必须要求是安全的。 //由于二级缓存的数据不一定都是存储到内存中,它的存储介质多种多样,所以需要给缓存的对象执行序列化。如果存储在内存中的话,实测不序列化也可以的。 public class User implements Serializable { private int id; private String name; private String pwd; }

  1. XML配置文件 =>

    • 默认配置

<cache/>

  • 自定义配置

<cache eviction="FIFO" flushInterval="3000" size="512" readOnly="true"/>

  1. 测试操作

@Test public void Test03(){ SqlSession sqlSession01 = MybatisUtils.getSqlSession(); SqlSession sqlSession02 = MybatisUtils.getSqlSession(); UserMapper mapper01 = sqlSession01.getMapper(UserMapper.class); User user01 = mapper01.queryUserById(2); System.out.println(user01.toString()); sqlSession01.close(); System.out.println("------------***************----------------"); UserMapper mapper02 = sqlSession02.getMapper(UserMapper.class); User user02 = mapper02.queryUserById(2); System.out.println(user02.toString()); System.out.println(user01==user02); sqlSession02.close(); }

第一次先不开启二级缓存

  • 运行结果如下:可以看出再不开启二级缓存的情况下,两个sqlsession产生的结果对象并不一致,且查询操作sql语句使用了两次

Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. Opening JDBC Connection Created connection 2130772866. ==> Preparing: select * from mybatis.user where id=? ==> Parameters: 2(Integer) <== Columns: id, name, pwd <== Row: 2, lisi, 123456 <== Total: 1 User(id=2, name=lisi, pwd=123456) Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7f010382] Returned connection 2130772866 to pool. ------------***************---------------- Opening JDBC Connection Checked out connection 2130772866 from pool. ==> Preparing: select * from mybatis.user where id=? ==> Parameters: 2(Integer) <== Columns: id, name, pwd <== Row: 2, lisi, 123456 <== Total: 1 User(id=2, name=lisi, pwd=123456) false Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7f010382] Returned connection 2130772866 to pool. 进程已结束,退出代码0

第二次开启二级缓存

  • 结果如下:可以看出开启二级缓存后查询结果两个sqlsession产生的返回对象是同一个,且sql语句只调用了一次

Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. Cache Hit Ratio [com.zhang.dao.UserMapper]: 0.0 Opening JDBC Connection Created connection 963110412. ==> Preparing: select * from mybatis.user where id=? ==> Parameters: 2(Integer) <== Columns: id, name, pwd <== Row: 2, lisi, 123456 <== Total: 1 User(id=2, name=lisi, pwd=123456) Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3967e60c] Returned connection 963110412 to pool. ------------***************---------------- Cache Hit Ratio [com.zhang.dao.UserMapper]: 0.5 User(id=2, name=lisi, pwd=123456) true 进程已结束,退出代码0

注意如果不进行缓存只读设置很可能会刷新第一次查询的缓存记录

Mybatis中如何实现二级缓存机制?

  • 根据下方的测试结果一,可以看出我们不设置readOnly="true"时,返回的user01,user02和user03都是不同的,

二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新。

  • 根据下方的测试结果二,可以看出我们设置readOnly="true"时,返回的user01和user02是相同的,但user02和user03是不同的,

@Test public void Test03(){ SqlSession sqlSession01 = MybatisUtils.getSqlSession(); SqlSession sqlSession02 = MybatisUtils.getSqlSession(); UserMapper mapper01 = sqlSession01.getMapper(UserMapper.class); User user01 = mapper01.queryUserById(2); System.out.println(user01.toString()); sqlSession01.close(); System.out.println("------------***************----------------"); UserMapper mapper02 = sqlSession02.getMapper(UserMapper.class); User user02 = mapper02.queryUserById(2); System.out.println(user02.toString()); System.out.println(user01==user02); User user = new User(); user.setId(3); user.setPwd("6113081"); user.setName("FT"); int i = mapper02.updateUser(user); if (i >= 0) { System.out.println("更新成功"); sqlSession02.commit();//事务一旦提交就会刷新缓存 }else{ System.out.println("更新失败"); sqlSession02.close(); } User user03 = mapper02.queryUserById(2); System.out.println(user03==user02); sqlSession02.close(); }

测试结果一 Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. Cache Hit Ratio [com.zhang.dao.UserMapper]: 0.0 Opening JDBC Connection Created connection 2061347276. ==> Preparing: select * from mybatis.user where id=? ==> Parameters: 2(Integer) <== Columns: id, name, pwd <== Row: 2, lisi, 123456 <== Total: 1 User(id=2, name=lisi, pwd=123456) Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7adda9cc] Returned connection 2061347276 to pool. ------------***************---------------- As you are using functionality that deserializes object streams, it is recommended to define the JEP-290 serial filter. Please refer to docs.oracle.com/pls/topic/lookup?ctx=javase15&id=GUID-8296D8E8-2B93-4B9A-856E-0A65AF9B8C66 Cache Hit Ratio [com.zhang.dao.UserMapper]: 0.5 User(id=2, name=lisi, pwd=123456) false Opening JDBC Connection Checked out connection 2061347276 from pool. ==> Preparing: update mybatis.user set name=?,pwd=? where id=? ==> Parameters: FT(String), 6113081(String), 3(Integer) <== Updates: 1 更新成功 Cache Hit Ratio [com.zhang.dao.UserMapper]: 0.3333333333333333 ==> Preparing: select * from mybatis.user where id=? ==> Parameters: 2(Integer) <== Columns: id, name, pwd <== Row: 2, lisi, 123456 <== Total: 1 false Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7adda9cc] Returned connection 2061347276 to pool. 进程已结束,退出代码0

测试结果二 Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. Cache Hit Ratio [com.zhang.dao.UserMapper]: 0.0 Opening JDBC Connection Created connection 963110412. ==> Preparing: select * from mybatis.user where id=? ==> Parameters: 2(Integer) <== Columns: id, name, pwd <== Row: 2, lisi, 123456 <== Total: 1 User(id=2, name=lisi, pwd=123456) Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3967e60c] Returned connection 963110412 to pool. ------------***************---------------- Cache Hit Ratio [com.zhang.dao.UserMapper]: 0.5 User(id=2, name=lisi, pwd=123456) true Opening JDBC Connection Checked out connection 963110412 from pool. ==> Preparing: update mybatis.user set name=?,pwd=? where id=? ==> Parameters: FT(String), 6113081(String), 3(Integer) <== Updates: 1 更新成功 Cache Hit Ratio [com.zhang.dao.UserMapper]: 0.3333333333333333 ==> Preparing: select * from mybatis.user where id=? ==> Parameters: 2(Integer) <== Columns: id, name, pwd <== Row: 2, lisi, 123456 <== Total: 1 false Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3967e60c] Returned connection 963110412 to pool. 进程已结束,退出代码0 1.2.4 小结

二级缓存是mapper级别的缓存,它的实现机制跟一级缓存差不多,也是基于PerpetualCache的HashMap本地存储。作用域为mapper的namespace,可以自定义存储,比如Ehcache。Mybatis的二级缓存是跨Session的,每个Mapper享有同一个二级缓存域。
Mybatis内部存储缓存使用一个HashMap,key为hashCode+sqlId+Sql语句。value为从查询出来映射生成的Java对象。

本文共计2813个文字,预计阅读时间需要12分钟。

Mybatis中如何实现二级缓存机制?

1.2 二级缓存【官方声明】=如何开启【二级缓存】+ 默认情况下,只启用了本地的会话缓存,它仅对单个会话中的数据进行缓存。它仅对单个会话中的数据进行缓存。若要启用全局的二级缓存,只需在您的 SQL 映射文件中添加相应的配置。

1.2 二级缓存
  • => 如何开启

    默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:

    • 在XML映射文件中添加以下代码,以开启

<cache/>

  • => 的作用

    • 映射语句文件中的所有 select 语句的结果将会被缓存。
    • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
    • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
    • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
    • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
    • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
  • => 的作用域

    • 缓存只作用于 cache 标签所在的映射文件中的语句。如果你混合使用 Java API 和 XML 映射文件,在共用接口中的语句将不会被默认缓存。你需要使用 @CacheNamespaceRef 注解指定缓存作用域。
  • => <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。

  • 总结
1.2.1 什么是二级缓存?

MyBatis的二级缓存是Application级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能。

二级缓存即使当一级缓存被清除/关闭也会存在(即sqlsession.close()方法执行后依旧会保存查询缓存)

SqlSessionFactory层面上的二级缓存默认是不开启的,二级缓存的开启需要进行配置,实现二级缓存的时候,MyBatis要求返回的POJO必须是可序列化的( 要求实现Serializable接口)

1.2.2 二级缓存的作用
  • 映射语句文件中的所有select语句将会被缓存。
  • 映射语句文件中的所有insert、update和delete语句会刷新缓存。
  • 缓存会使用默认的Least Recently Used(LRU,最近最少使用的)算法来收回。
  • 根据时间表,比如No Flush Interval,(CNFI没有刷新间隔),缓存不会以任何时间顺序来刷新。
  • 缓存会存储列表集合或对象(无论查询方法返回什么)的1024个引用
  • 缓存会被视为是read/write(可读/可写)的缓存,意味着对象检索不是共享的,而且可以安全的被调用者修改,不干扰其他调用者或线程所做的潜在修改。
1.2.3 测试
  1. 实现类 =>

@Data //实现二级缓存返回的pojo对象必须要求是安全的。 //由于二级缓存的数据不一定都是存储到内存中,它的存储介质多种多样,所以需要给缓存的对象执行序列化。如果存储在内存中的话,实测不序列化也可以的。 public class User implements Serializable { private int id; private String name; private String pwd; }

  1. XML配置文件 =>

    • 默认配置

<cache/>

  • 自定义配置

<cache eviction="FIFO" flushInterval="3000" size="512" readOnly="true"/>

  1. 测试操作

@Test public void Test03(){ SqlSession sqlSession01 = MybatisUtils.getSqlSession(); SqlSession sqlSession02 = MybatisUtils.getSqlSession(); UserMapper mapper01 = sqlSession01.getMapper(UserMapper.class); User user01 = mapper01.queryUserById(2); System.out.println(user01.toString()); sqlSession01.close(); System.out.println("------------***************----------------"); UserMapper mapper02 = sqlSession02.getMapper(UserMapper.class); User user02 = mapper02.queryUserById(2); System.out.println(user02.toString()); System.out.println(user01==user02); sqlSession02.close(); }

第一次先不开启二级缓存

  • 运行结果如下:可以看出再不开启二级缓存的情况下,两个sqlsession产生的结果对象并不一致,且查询操作sql语句使用了两次

Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. Opening JDBC Connection Created connection 2130772866. ==> Preparing: select * from mybatis.user where id=? ==> Parameters: 2(Integer) <== Columns: id, name, pwd <== Row: 2, lisi, 123456 <== Total: 1 User(id=2, name=lisi, pwd=123456) Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7f010382] Returned connection 2130772866 to pool. ------------***************---------------- Opening JDBC Connection Checked out connection 2130772866 from pool. ==> Preparing: select * from mybatis.user where id=? ==> Parameters: 2(Integer) <== Columns: id, name, pwd <== Row: 2, lisi, 123456 <== Total: 1 User(id=2, name=lisi, pwd=123456) false Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7f010382] Returned connection 2130772866 to pool. 进程已结束,退出代码0

第二次开启二级缓存

  • 结果如下:可以看出开启二级缓存后查询结果两个sqlsession产生的返回对象是同一个,且sql语句只调用了一次

Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. Cache Hit Ratio [com.zhang.dao.UserMapper]: 0.0 Opening JDBC Connection Created connection 963110412. ==> Preparing: select * from mybatis.user where id=? ==> Parameters: 2(Integer) <== Columns: id, name, pwd <== Row: 2, lisi, 123456 <== Total: 1 User(id=2, name=lisi, pwd=123456) Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3967e60c] Returned connection 963110412 to pool. ------------***************---------------- Cache Hit Ratio [com.zhang.dao.UserMapper]: 0.5 User(id=2, name=lisi, pwd=123456) true 进程已结束,退出代码0

注意如果不进行缓存只读设置很可能会刷新第一次查询的缓存记录

Mybatis中如何实现二级缓存机制?

  • 根据下方的测试结果一,可以看出我们不设置readOnly="true"时,返回的user01,user02和user03都是不同的,

二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新。

  • 根据下方的测试结果二,可以看出我们设置readOnly="true"时,返回的user01和user02是相同的,但user02和user03是不同的,

@Test public void Test03(){ SqlSession sqlSession01 = MybatisUtils.getSqlSession(); SqlSession sqlSession02 = MybatisUtils.getSqlSession(); UserMapper mapper01 = sqlSession01.getMapper(UserMapper.class); User user01 = mapper01.queryUserById(2); System.out.println(user01.toString()); sqlSession01.close(); System.out.println("------------***************----------------"); UserMapper mapper02 = sqlSession02.getMapper(UserMapper.class); User user02 = mapper02.queryUserById(2); System.out.println(user02.toString()); System.out.println(user01==user02); User user = new User(); user.setId(3); user.setPwd("6113081"); user.setName("FT"); int i = mapper02.updateUser(user); if (i >= 0) { System.out.println("更新成功"); sqlSession02.commit();//事务一旦提交就会刷新缓存 }else{ System.out.println("更新失败"); sqlSession02.close(); } User user03 = mapper02.queryUserById(2); System.out.println(user03==user02); sqlSession02.close(); }

测试结果一 Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. Cache Hit Ratio [com.zhang.dao.UserMapper]: 0.0 Opening JDBC Connection Created connection 2061347276. ==> Preparing: select * from mybatis.user where id=? ==> Parameters: 2(Integer) <== Columns: id, name, pwd <== Row: 2, lisi, 123456 <== Total: 1 User(id=2, name=lisi, pwd=123456) Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7adda9cc] Returned connection 2061347276 to pool. ------------***************---------------- As you are using functionality that deserializes object streams, it is recommended to define the JEP-290 serial filter. Please refer to docs.oracle.com/pls/topic/lookup?ctx=javase15&id=GUID-8296D8E8-2B93-4B9A-856E-0A65AF9B8C66 Cache Hit Ratio [com.zhang.dao.UserMapper]: 0.5 User(id=2, name=lisi, pwd=123456) false Opening JDBC Connection Checked out connection 2061347276 from pool. ==> Preparing: update mybatis.user set name=?,pwd=? where id=? ==> Parameters: FT(String), 6113081(String), 3(Integer) <== Updates: 1 更新成功 Cache Hit Ratio [com.zhang.dao.UserMapper]: 0.3333333333333333 ==> Preparing: select * from mybatis.user where id=? ==> Parameters: 2(Integer) <== Columns: id, name, pwd <== Row: 2, lisi, 123456 <== Total: 1 false Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7adda9cc] Returned connection 2061347276 to pool. 进程已结束,退出代码0

测试结果二 Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. Cache Hit Ratio [com.zhang.dao.UserMapper]: 0.0 Opening JDBC Connection Created connection 963110412. ==> Preparing: select * from mybatis.user where id=? ==> Parameters: 2(Integer) <== Columns: id, name, pwd <== Row: 2, lisi, 123456 <== Total: 1 User(id=2, name=lisi, pwd=123456) Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3967e60c] Returned connection 963110412 to pool. ------------***************---------------- Cache Hit Ratio [com.zhang.dao.UserMapper]: 0.5 User(id=2, name=lisi, pwd=123456) true Opening JDBC Connection Checked out connection 963110412 from pool. ==> Preparing: update mybatis.user set name=?,pwd=? where id=? ==> Parameters: FT(String), 6113081(String), 3(Integer) <== Updates: 1 更新成功 Cache Hit Ratio [com.zhang.dao.UserMapper]: 0.3333333333333333 ==> Preparing: select * from mybatis.user where id=? ==> Parameters: 2(Integer) <== Columns: id, name, pwd <== Row: 2, lisi, 123456 <== Total: 1 false Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3967e60c] Returned connection 963110412 to pool. 进程已结束,退出代码0 1.2.4 小结

二级缓存是mapper级别的缓存,它的实现机制跟一级缓存差不多,也是基于PerpetualCache的HashMap本地存储。作用域为mapper的namespace,可以自定义存储,比如Ehcache。Mybatis的二级缓存是跨Session的,每个Mapper享有同一个二级缓存域。
Mybatis内部存储缓存使用一个HashMap,key为hashCode+sqlId+Sql语句。value为从查询出来映射生成的Java对象。