熟悉MyBatis的小伙伴都知道MyBatis默认开启一级缓存,当我们执行一条查询语句后,MyBatis会以我们查询的信息生成一个缓存key,查询的结果为value,存到一个map中,即存入一级缓存。
环境:Mybatis + Spring,MyBatis的一级缓存使用默认值,即开启一级缓存
测试1
在一个serviceImpl中连续调用三次dao层查询数据库
现象
日志显示,创建了三个sqlSession,查询了三次数据库
结论
MyBatis的一级缓存没起作用
测试2
我们在serviceImpl的方法上加入 @Transactional
现象2
日志显示,创建了一个sqlSession,查询了一次数据库
结论2
MyBatis的一级缓存又起作用了
结论:Spring将MyBatis的DefaultSqlSession类替换成了SqlSessionTemplate。
MyBatis的一级缓存是基于SqlSession来实现的,对应MyBatis中sqlSession接口的默实认现类是DefaultSqlSession,如果执行的SQL相同时,并且使用的是同一个SqlSession对象,那么就会触发对应的缓存机制。
但是在Spring整合MyBatis后,Spring使用MyBatis不再是直接调用MyBatis中的信息,而是通过调用调用mybatis-spring.jar中的类,继而达到间接调用MyBatis的效果。但在mybatis-spring.jar中,引入了一个SqlSessionTemplate类,它和Spring的事务管理器共同配合,创建对应的SqlSession连接。
即在没有添加@Transactional注解的情况下,每调用一次查询SQL,就会通过SqlSessionTemplate去创建sqlSession,即相当于新创建一次连接,故而每次查询在调试结果看来就是一级缓存失效
为什么加了@Transactional注解就可以使用缓存?
核心就是注册的方法,我测试的场景是没有加@Transactional注解的时候,此处判断为false就不会再向缓存中添加数据。
当然如果判断成功就是会调用TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory))方法,将该sqlSession对象添加到对应的缓存中,数量+1
即最终注册到synchronizations对象的缓存中
缓存池使用的是一个ThreadLocal(用于处理多个线程中数据的隔离问题,内部维护一个ThreadLocalMap)来存储
synchronizations = new NamedThreadLocal<>(“Transaction synchronizations”);
总结