Mybatis-缓存解析

一级缓存

在mybatis中一级缓存是默认开启的,它以sqlsession’为单位,多个sqlsession的缓存相互独立,互不干扰

Mybatis-缓存解析

一级缓存的生命周期

  1. MyBatis 在开启一个数据库会话时,会创建一个新的 SqlSession 对象,SqlSession 对象中
    会有一个新的 Executor 对象。Executor 对象中持有一个新的 PerpetualCache 对象;当会
    话结束时,SqlSession 对象及其内部的 Executor 对象还有 PerpetualCache 对象也一并释
    放掉。
  2. 如果 SqlSession 调用了 close()方法,会释放掉一级缓存 PerpetualCache 对象,一级缓存
    将不可用。
  3. 如果 SqlSession 调用了 clearCache(),会清空 PerpetualCache 对象中的数据,但是该对象
    仍可使用。
  4. SqlSession 中执行了任何一个 update 操作(update()、delete()、insert()) ,都会清空
    PerpetualCache 对象的数据,但是该对象可以继续使用

简而言之,mybatis在面临查询时,会查找一级缓存中是否有相同的查询条件,若有则从缓冲池中拿数据,没有则从数据库中查找,并将结果存储在一级缓存中

如何判断查询是否相同

  1. 传入的 statementId。

  2. 查询时要求的结果集中的结果范围。

  3. 这次查询所产生的最终要传递给 Preparedstatement 的 Sql 语句字符串。

  4. 传递的参数

根据以上四个条件可以判断查询是否相同,但其实简单来说根据statementId和传递参数就能判断查询条件是否相同

一级缓存可能出现的问题

我们知道,一级缓存的缓冲池是以sqlsession为单位的,当多个sqlsession存在时,若一个sqlsession改变了一条记录,而另一个sqlsession在修改前已经将该记录存放在缓冲池中,此时缓冲池的数据已经跟数据库的真实数据不一样了。
所以一级缓存无法保证数据的实时性。

二级缓存

二级缓存是以sqlsessionFactory为单位的,每个mapper的namespace对应一个cache,可以使用cache-ref让多个mapper指向同一个cache,下面会解释为什么要这样做

Mybatis-缓存解析
注意:执行commit或close的时候才会将一级缓冲的数据同步到二级缓存,sqlsession在二级缓存中查不到该数据会去一级缓存中查找,再不行才会查找数据库

二级缓存的作用

我们知道,一级缓存的作用范围是sqlsession,若其它sqlsession将数据进行更改后而当前的sqlsession未能及时察觉,查询出来的数据已经不是真实数据了,二级缓存的作用就是让多个sqlsession能够相互通信,避免上述情况的发生。

下面给出一些情况

1. 当多个sqlsession从二级缓存中查询数据时,一个sqlsession对其修改后,其它sqlsession也能及时更新

 		SqlSession session = factory.openSession();
        SqlSession session2 = factory.openSession();
        SqlSession session3 = factory.openSession();
        StuMapper mapper = session.getMapper(StuMapper.class);
        StuMapper mapper2 = session2.getMapper(StuMapper.class);
        StuMapper mapper3 = session3.getMapper(StuMapper.class);
        
        //往二级缓冲池中放数据
        Student student = mapper.selById2(1);
        System.out.println(student);
        session.commit();
        System.out.println("-----------------------------------------------");
        
        //从二级缓存中拿数据
        Student student2 = mapper2.selById2(1);
        System.out.println(student2);
        System.out.println("-----------------------------------------------");
        
        //清空该mapper对应的二级缓存池
        mapper.updStu(1,"cx");
        session.commit();
        
        //重新从数据库查询数据
        Student student3 = mapper3.selById2(1);
        System.out.println(student3);
        System.out.println("-----------------------------------------------");

Mybatis-缓存解析

2. 当查询涉及到多表连接时,其中一张表对应的mapper执行DML操作,而另一个mapper对应的二级缓存池不会清空,除非将两个mapper的namespace绑定为同一个

 SqlSession session = factory.openSession();
        SqlSession session2 = factory.openSession();
        SqlSession session3 = factory.openSession();
        StuMapper mapper = session.getMapper(StuMapper.class);
        StuMapper mapper2 = session2.getMapper(StuMapper.class);
        StuMapper mapper3 = session3.getMapper(StuMapper.class);
        ClaMapper claMapper = session.getMapper(ClaMapper.class);

        Student student = mapper.selById2(1);
        System.out.println(student);
        //更新班级名称
        claMapper.updateCla(1,"web");
        session.commit();

        Student student2 = mapper2.selById2(1);
        System.out.println(student2);
        session2.commit();


        Student student3 = mapper3.selById2(1);
        System.out.println(student3);
        Student student4 = mapper3.selById2(1);
        System.out.println(student4);
        session3.commit();
        Student student5 = mapper2.selById2(1);
        System.out.println(student5);

Mybatis-缓存解析

在ClaMapper中加上<cache-ref namespace=“com.xie.stuMapper”>后
Mybatis-缓存解析
这样做有利有弊,利是能够更好的保证数据的实时性,弊端则是增大了检测的力度,使得缓存更容易被清空,降低了效率

3. 当一个sqlsession1一级缓存已经有记录A时,其他sqlsession对记录A进行修改虽然会清空二级缓存,但sqlsession1查询时仍然会从一级缓存中拿到未被修改的A

上一篇:【Mybatis】笔记(一)


下一篇:MyBatis缓存机制