MyBatis一般都是指二级缓存。一级缓存(也叫本地缓存)默认会启用,并且不能控制

7.1 一级缓存

  1. @Test
  2. public void testL1Cache() {
  3. SqlSession sqlSession = getSqlSession();
  4. SysUser user1 = null;
  5. try {
  6. UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
  7. user1 = userMapper.selectById(1L);
  8. user1.setUserName("chl");
  9. SysUser user2 = userMapper.selectById(1L);
  10. Assert.assertEquals("chl", user2.getUserName());
  11. Assert.assertEquals(user1, user2);
  12. }finally {
  13. sqlSession.close();
  14. }
  15. System.out.println("开启新的sqlSession");
  16. sqlSession = getSqlSession();
  17. try {
  18. UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
  19. SysUser user2 = userMapper.selectById(1L);
  20. Assert.assertNotEquals("chl", user2.getUserName());
  21. Assert.assertNotEquals(user1, user2);
  22. userMapper.deleteById(2L);
  23. SysUser user3 = userMapper.selectById(1L);
  24. Assert.assertNotEquals(user2, user3);
  25. }finally {
  26. sqlSession.rollback();
  27. sqlSession.close();
  28. }
  29. }

image.png
MyBatis的一级缓存存在于SqlSession的声明周期中,在同一个SqlSession查询时,MyBatis会把执行的方法和参数通过算法生成缓存的键值,将键值和查询结果存入一个Map对象中。如果同一个SqlSession中执行的方法和参数完全一致,那么通过算法会生成相同的键值,当Map缓存对象中已经存在该键值时,则会返回缓存中的对象
进行如下修改,可以避免使用一级缓存

  1. <select id="selectById" resultMap="userMap" flushCache="true">
  2. select * from sys_user where id = #{id}
  3. </select>

任何的insert、update、delete操作都会清空一级缓存

7.2 二级缓存

可以理解为存在于SqlSessionFactory的生命周期中

7.2.1 配置二级缓存

mybatis-config

  1. <setting name="cacheEnabled" value="true"/>

该配置默认为true
MyBatis的二级缓存是和命名空间绑定的,即需要配置在Mapper.xml中或Mapper.java中

7.2.1.1 Mapper.xml中配置二级缓存

image.png
只需添加<cache/>
默认的二级缓存会有如下效果:

  • select语句会被缓存
  • insert、update、delete会刷新缓存
  • 会用Least Recently Used(LRU)算法来回收
  • 根据时间表,缓存不会以任何时间顺序来刷新
  • 缓存会存储集合或对象的1024个引用
  • 缓存会被视为read/write的,意味着对象检索不是共享的,而且可以安全地被调用者修改

所有这些属性都可以通过cache元素的属性来修改

  1. <cache
  2. eviction="FIFO"
  3. flushInterval="60000"
  4. size="512"
  5. readOnly="true"/>

7.2.1.2 Mapper接口中配置二级缓存

在使用注解方法时,如果想对注解方法启用二级缓存,则需要在Mapper接口中进行配置@CacheNamespace
image.png
当同时使用注解和xml方式时,不能同时配置上述的二级缓存,这时可以用参照缓存@CacheNamespaceRef(RoleMapper.class)
在xml中也可以配置参照缓存:<cache-ref namespace="com.ql.simple.mapper.RoleMapper"/>,参照缓存不仅能引用其他缓存减少配置外,主要是为了解决脏读

7.2.2 使用二级缓存

如果配置的可读写缓存,MyBatis使用SerializedCache序列化缓存来实现可读写缓存类
如果为只读缓存,MyBatis就会使用Map来存储缓存值
当使用可读写缓存的时候,这个缓存类要求所有被序列化的对象必须实现Serializable
image.png

  1. @Test
  2. public void testL2Cache() {
  3. SqlSession sqlSession = getSqlSession();
  4. SysRole role1 = null;
  5. try {
  6. RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
  7. role1 = roleMapper.selectRoleById(1L);
  8. role1.setRoleName("chl");
  9. SysRole role2 = roleMapper.selectRoleById(1L);
  10. Assert.assertEquals("chl", role2.getRoleName());
  11. Assert.assertEquals(role1, role2);
  12. }finally {
  13. sqlSession.close();
  14. }
  15. System.out.println("开启新的SqlSession");
  16. sqlSession = getSqlSession();
  17. try {
  18. RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
  19. SysRole role2 = roleMapper.selectRoleById(1L);
  20. Assert.assertEquals("chl", role2.getRoleName());
  21. Assert.assertNotEquals(role1, role2);
  22. SysRole role3 = roleMapper.selectRoleById(1L);
  23. Assert.assertNotEquals(role2, role3);
  24. }finally {
  25. sqlSession.close();
  26. }
  27. }

7.3 集成EhCache缓存

  1. <dependency>
  2. <groupId>org.mybatis.caches</groupId>
  3. <artifactId>mybatis-ehcache</artifactId>
  4. <version>1.0.3</version>
  5. </dependency>
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:noNamespaceSchemaLocation="ehcache.xsd"
  4. updateCheck="false" monitoring="autodetect"
  5. dynamicConfig="true">
  6. <diskStore path="D:/cache" />
  7. <defaultCache
  8. maxElementsInMemory="3000"
  9. eternal="false"
  10. copyOnRead="true"
  11. copyOnWrite="true"
  12. timeToIdleSeconds="3600"
  13. timeToLiveSeconds="3600"
  14. overflowToDisk="true"
  15. diskPersistent="true"/>
  16. <cache
  17. name="tk.mybatis.simple.mapper.RoleMapper"
  18. maxElementsInMemory="3000"
  19. eternal="false"
  20. copyOnRead="true"
  21. copyOnWrite="true"
  22. timeToIdleSeconds="3600"
  23. timeToLiveSeconds="3600"
  24. overflowToDisk="true"
  25. diskPersistent="true"/>
  26. </ehcache>
  • copyOnRead: 判断缓存中读取数据是返回对象引用还是复制一个对象返回。默认为false,即返回数据引用
  • copyOnWrite:判断写入缓存是直接缓存对象的引用还是复制一个对象缓存,默认为false,如果想使用可读写缓存,就需要将这两个属性设置为true

ehcache-cache提供了两个缓存实现:

  • EhcacheCache
  • LoggingEhcache

上面两个缓存使用时并没有区别,都会输出缓存命中率的日志

  1. <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
  1. <cache
  2. name="tk.mybatis.simple.mapper.RoleMapper"
  3. maxElementsInMemory="3000"
  4. eternal="false"
  5. copyOnRead="true"
  6. copyOnWrite="true"
  7. timeToIdleSeconds="3600"
  8. timeToLiveSeconds="3600"
  9. overflowToDisk="true"
  10. diskPersistent="true"/>

7.4 集成Redis缓存

7.5 脏数据的产生和避免