首页 > 技术文章 > Mybatis缓存配置—— 二级缓存

yeyuting 2020-12-29 09:27 原文

一、配置二级缓存

1. 在mybatis_config.xml中进行如下配置:

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

其实这里的二级缓存默认是出于开启状态,因此这个位置可以不进行配置,知道有这么回事儿即可。

2.MyBatis二级缓存是和命名空间是绑定的 ,即二级缓存需要配置在 Mapper.xml 映射文件中,或者配置在 Mapper.java 接口中。在映射文件中命名空间就是 XML 根节点 mapper namespace 属性 Mapper 接口中,命名空间就是接口的全限定名称。

(1)在RoleMapper.xml中进行二级缓存配置,添加配置如下:

<?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="com.example.simple.mapper.RoleMapper">
    <cache
    eviction="FIFO"
    flushInterval="6000"
    size="512"
    readOnly="false"/>
/mapper>

(2)在RoleMapper.java类中添加注解:

@CacheNamespaceRef(RoleMapper.class)
public interface RoleMapper {
//代码
}

最基本的二级缓存就配置好了。

二、使用二级缓存

  上面的配置是可读写的缓存,而 MyBatis使用 SerializedCache序列化缓存来实现可读写缓存类,井通过序列化和反序列化来保证通过缓存获取数据时,得到的是一个新的实例。因此,如果配置为只读缓存,Mybatis就会使用 Map 来存储缓存值,这种情况下 ,从缓存中获取的对象就是同一个实例。

1. 因为使用可读写缓存,可以使用 SerializedCache 序列 缓存。这个缓存类要求所有被序列化的对象必须实现 Serializable (java.io.Serializable )接口,所以还需要修改 SysRole 对象 ,代码如下:
public class SysRole implements Serializable
{

    private static final long serialVersionUID = 6320941908222932112L ;

    /*
    * 其他方法    * */
}

2. 编写测试方法如下:

@Test
        public void testCache2(){
            SqlSession sqlSession = getSqlSession();
            SysRole role = null;
            try{
                RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
                role = roleMapper.selectById(1l);
                role.setRoleName("new name");
                SysRole role1 = roleMapper.selectById(1l);
                Assert.assertEquals(role1.getRoleName() , "new name");
                Assert.assertEquals(role,role1);

            }finally {
                sqlSession.close();
            }
            System.out.println("开启新一个sqlSession" );
            sqlSession = getSqlSession();
            try{
                RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
                SysRole role1 = roleMapper.selectById(1l);
                Assert.assertEquals(role1.getRoleName(),"new name");
                Assert.assertNotEquals(role,role1);
                SysRole role2 = roleMapper.selectById(1l);
                Assert.assertNotEquals(role1,role2);
            }finally {

            }

        }

测试结果如下:

DEBUG [main] - Cache Hit Ratio [com.example.simple.mapper.RoleMapper]: 0.0
DEBUG [main] - ==> Preparing: select id, role_name,enabled,create_by,create_time from sys_role where id = ?
DEBUG [main] - ==> Parameters: 1(Long)
TRACE [main] - <== Columns: id, role_name, enabled, create_by, create_time
TRACE [main] - <== Row: 1, 管理员, 1, 1, 2020-12-01 20:05:01
DEBUG [main] - <== Total: 1
DEBUG [main] - Cache Hit Ratio [com.example.simple.mapper.RoleMapper]: 0.0
开启新一个sqlSession
DEBUG [main] - Cache Hit Ratio [com.example.simple.mapper.RoleMapper]: 0.3333333333333333
DEBUG [main] - Cache Hit Ratio [com.example.simple.mapper.RoleMapper]: 0.5

解析:

  日志中存在好几条以 Cache Hit Ratio 开头的语句 ,这行日志后面输出的值为当前执行方法的缓存命中率,在测试第一部分中,第一次查询获取 rolel 的时候由于没有缓存,所以执

行了数据库查询。在第二个查询获取 role2 的时候, role2和rolel 是完全相同的实例,这里使用的是一级缓存,所以返回同一个实例。
  调用 close 方法关闭 SqlSession 时, SqlSession 才会保存查询数据到二级缓存中,在这之后二级缓存才有了缓存数据,所以可以看到在第一部分的两次查询时,命中率都是0;在第二部分测试代码中,再次获取 role2 时,日志中并没有输出数据库查询,而是输出了命中率,这时的命中率是 0.3333333333333333 ,这是第三次查询,并且得到了缓存的值,因此该方法 共被请求了3次,有一次命中,所以命中率就是三分之一。 后面再获取 role3 的时候,就是四次请求,两次命中,命中率为 0.5 。并且因为可读写缓存的缘故, role2和role3 都是反序列化得到的结果 ,所以它们不是相同的实例,在这 部分,这两个实例是读写安全的,其属性不会互相影响。
三、二级缓存使用场景:
级缓存虽然好处很多,但并不是什么时候都可以使用在以下场景中,推荐使用二级缓存以查询为主的应用中,只有尽可能少的增、删、改操作,绝大多数以单表操作存在时,由于很少存在互相关联的情况,因此不会出现脏数据可以按业务划分对表进行分组时,如关联的表比较少,可以通过参照缓存进行配置。除了推荐使用的情况,如果脏读对系统没有影响,也可以考虑使用。在无法保证数据不出现脏读的情况下,建议在业务层使用可控制的缓存代替二级缓存
  至此,告一段落。

推荐阅读