Mybatis 一级缓存与二级缓存(用例测试)

一级缓存:

Mybatis 一级缓存作用域是一个sqlsession(缓存属于当前会话独享),即一次sqlsession从打开到关闭,这个过程中,重复的查询语句只执行一次,第一次查询会将结果放入一级缓存,sqlsession关闭之前,重复的查询语句将直接读取缓存。
当发生增删改操作时,在事务commit前会先清空sqlsession的缓存数据(此时清空的只是当前sqlsession的缓存,其他sqlsession已经查询到缓存的数据并不会被清除)。

二级缓存:

Mybatis二级缓存的作用域是一个命名空间,即多个sqlsession在使用同一个命名空间下的查询语句时,会先从二级缓存获取,查找不到则从一级缓存获取,再获取不到则从数据库获取。
sqlsession将数据存入二级缓存的时机是在sqlsession关闭后,即当前sqlsession查询了数据,在当前sqlsession关闭前,都不会将数据存入二级缓存。
当sqlsession 更新了数据时,事务commit后依然不会改变二级缓存的数据,只有当前sqlsession关闭后,才会删除当前命名空间内的二级缓存(注意,如果事务提交后未关闭sqlsession,又查询了记录,则关闭sqlsession时,会删除二级缓存,也会将事务提交后的查询结果存入二级缓存)。

例:
开启缓存的配置:

    <settings>
        <!-- 一级缓存 -->
        <setting name="localCacheScope" value="SESSION"/>
        <!-- 二级缓存 -->
        <setting name="cacheEnabled" value="true"/>
        <!--开启驼峰式命名,数据库的列名能够映射到去除下划线驼峰命名后的字段名-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <setting name="logImpl" value="LOG4J"/>
    </settings>

测试代码1(数据库和实体类代码就不贴了):

 @Test
  public void testCacheWithoutCommit() throws Exception {
      SqlSession sqlSession1 = factory.openSession(true); // 自动提交事务
      SqlSession sqlSession2 = factory.openSession(true); // 自动提交事务

      StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class);
      StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);

      //数据存入一级缓存
      System.out.println("studentMapper1读取数据: " + studentMapper.getStudentById(1));
      System.out.println("studentMapper2读取数据: " + studentMapper2.getStudentById(1));

      //会话1读取自己的缓存
      System.out.println("studentMapper1读取数据: " + studentMapper.getStudentById(1));
      //会话2关闭,数据存入二级缓存
      sqlSession2.close();

      //会话1读取二级缓存
      System.out.println("studentMapper1读取数据: " + studentMapper.getStudentById(1));

    }

结果1:

DEBUG [main] - Cache Hit Ratio [mapper.StudentMapper]: 0.0
DEBUG [main] - ==>  Preparing: SELECT id,name,age FROM student WHERE id = ? 
DEBUG [main] - ==> Parameters: 1(Integer)
TRACE [main] - <==    Columns: id, name, age
TRACE [main] - <==        Row: 1, Amy, 16
DEBUG [main] - <==      Total: 1
studentMapper1读取数据: StudentEntity{id=1, name='Amy', age=16, className='null'}
DEBUG [main] - Cache Hit Ratio [mapper.StudentMapper]: 0.0
DEBUG [main] - ==>  Preparing: SELECT id,name,age FROM student WHERE id = ? 
DEBUG [main] - ==> Parameters: 1(Integer)
TRACE [main] - <==    Columns: id, name, age
TRACE [main] - <==        Row: 1, Amy, 16
DEBUG [main] - <==      Total: 1
studentMapper2读取数据: StudentEntity{id=1, name='Amy', age=16, className='null'}
DEBUG [main] - Cache Hit Ratio [mapper.StudentMapper]: 0.0
studentMapper1读取数据: StudentEntity{id=1, name='Amy', age=16, className='null'}
DEBUG [main] - Cache Hit Ratio [mapper.StudentMapper]: 0.25
studentMapper1读取数据: StudentEntity{id=1, name='Amy', age=16, className='null'}

打印结果中,前两次查询均从数据库获取数据,并存入一级缓存,所以第三条查询中,会话1查询时,没有打印sql语句,是从自己的一级缓存中获取的数据。
第四条查询中,DEBUG [main] - Cache Hit Ratio [mapper.StudentMapper]: 0.25,可以看到日志最后有一个0.25,这是二级缓存的命中率,我们总共查询了四次,只有最后一次是从二级缓存获取数据,所以命中率为0.25。

测试代码2:

    @Test
    public void testCacheWithCommit() throws Exception {
        SqlSession sqlSession1 = factory.openSession(true); // 自动提交事务
        SqlSession sqlSession2 = factory.openSession(true); // 自动提交事务

        StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class);
        StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);

        //数据分别存入两个会话的一级缓存,都会查询一次数据库(第一二次查询)
        System.out.println("studentMapper1读取数据: " + studentMapper.getStudentById(1));
        System.out.println("studentMapper2读取数据: " + studentMapper2.getStudentById(1));

        //会话2更新数据,将会话2的一级缓存清空
        System.out.println("studentMapper2更新了" + studentMapper2.updateStudentName("Tom",1) + "个学生的数据");

        //一级缓存已经被清空,重新在从数据库读取数据(第三次查询)
        System.out.println("studentMapper2读取数据: " + studentMapper2.getStudentById(1));

        //会话1取到他的一级缓存,数据是修改前的(第四次查询)
        System.out.println("studentMapper1读取数据: " + studentMapper.getStudentById(1));

        //会话2关闭,将事务提交后读取的数据存入二级缓存
        sqlSession2.close();
        //会话1读取二级缓存(第五次查询)
        System.out.println("studentMapper1读取数据: " + studentMapper.getStudentById(1));

    }

结果2:

DEBUG [main] - Cache Hit Ratio [mapper.StudentMapper]: 0.0
DEBUG [main] - ==>  Preparing: SELECT id,name,age FROM student WHERE id = ? 
DEBUG [main] - ==> Parameters: 1(Integer)
TRACE [main] - <==    Columns: id, name, age
TRACE [main] - <==        Row: 1, Amy, 16
DEBUG [main] - <==      Total: 1
studentMapper1读取数据: StudentEntity{id=1, name='Amy', age=16, className='null'}
DEBUG [main] - Cache Hit Ratio [mapper.StudentMapper]: 0.0
DEBUG [main] - ==>  Preparing: SELECT id,name,age FROM student WHERE id = ? 
DEBUG [main] - ==> Parameters: 1(Integer)
TRACE [main] - <==    Columns: id, name, age
TRACE [main] - <==        Row: 1, Amy, 16
DEBUG [main] - <==      Total: 1
studentMapper2读取数据: StudentEntity{id=1, name='Amy', age=16, className='null'}
DEBUG [main] - ==>  Preparing: UPDATE student SET name = ? WHERE id = ? 
DEBUG [main] - ==> Parameters: Tom(String), 1(Integer)
DEBUG [main] - <==    Updates: 1
studentMapper2更新了1个学生的数据
DEBUG [main] - Cache Hit Ratio [mapper.StudentMapper]: 0.0
DEBUG [main] - ==>  Preparing: SELECT id,name,age FROM student WHERE id = ? 
DEBUG [main] - ==> Parameters: 1(Integer)
TRACE [main] - <==    Columns: id, name, age
TRACE [main] - <==        Row: 1, Tom, 16
DEBUG [main] - <==      Total: 1
studentMapper2读取数据: StudentEntity{id=1, name='Tom', age=16, className='null'}
DEBUG [main] - Cache Hit Ratio [mapper.StudentMapper]: 0.0
studentMapper1读取数据: StudentEntity{id=1, name='Amy', age=16, className='null'}
DEBUG [main] - Cache Hit Ratio [mapper.StudentMapper]: 0.2
studentMapper1读取数据: StudentEntity{id=1, name='Tom', age=16, className='null'}

从打印结果可以看到,前两次同样是数据库查询,接着进行了数据更新操作(将Amy变成了Tom),此时提交了事务,并清空了会话2的一级缓存,所以在第三次查询时,会话2的一级缓存为空,重新读取数据库数据。
第四次查询是会话1的查询,虽然会话2更新了数据,但会话1读取的数据并未删除,所以读到的是会话1中的一级缓存(即Amy)。
第五次查询,此时会话2已经关闭(因为执行了sqlSession2.close();),所以会话2在事务提交后的查询结果存入了二级缓存,因此会话1查询到了二级缓存中的Tom。
总共5次查询,仅最后一次查询二级缓存,因此缓存命中率为0.2(Cache Hit Ratio [mapper.StudentMapper]: 0.2)。

以上仅是两个简单的例子,还有一些特殊情况大家可以自己试试。

比如两个命名空间同时操作一张表,一个命名空间内的会话更新了数据,则该命名空间的二级缓存数据清空,但另一个命名空间的二级缓存没有改变,另一个命名空间中同一条查询语句将会得到未更新的数据。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值