熟悉MyBatis的小伙伴都知道MyBatis默认开启一级缓存,当我们执行一条查询语句后,MyBatis会以我们查询的信息生成一个缓存key,查询的结果为value,存到一个map中,即存入一级缓存。
环境:Mybatis + Spring,MyBatis的一级缓存使用默认值,即开启一级缓存
测试1
在一个serviceImpl中连续调用三次dao层查询数据库
@GetMapping("/test")
public void test() throws JsonProcessingException {
List user = userService.getUser();
List user1 = userService.getUser();
List user2 = userService.getUser();
}
现象
日志显示,创建了三个sqlSession,查询了三次数据库
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7e8fe6ec] was not registered for synchronization because synchronization is not active
JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8357a79] will not be managed by Spring
==> Preparing: SELECT id,username,password FROM user
==> Parameters:
<== Columns: id, username, password
<== Row: 1, 李显赫, 1343
<== Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7e8fe6ec]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@64cc7639] was not registered for synchronization because synchronization is not active
JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8357a79] will not be managed by Spring
==> Preparing: SELECT id,username,password FROM user
==> Parameters:
<== Columns: id, username, password
<== Row: 1, 李显赫, 1343
<== Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@64cc7639]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@47ede809] was not registered for synchronization because synchronization is not active
JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8357a79] will not be managed by Spring
==> Preparing: SELECT id,username,password FROM user
==> Parameters:
<== Columns: id, username, password
<== Row: 1, 李显赫, 1343
<== Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@47ede809]
结论
MyBatis的一级缓存没起作用
测试2
我们在serviceImpl的方法上加入 @Transactional
@GetMapping("/test2")
@Transactional
public void test() throws JsonProcessingException {
List user = userService.getUser();
List user1 = userService.getUser();
List user2 = userService.getUser();
}
现象2
日志显示,创建了一个sqlSession,查询了一次数据库
Creating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4d29be04]
JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7e9f4a27] will be managed by Spring
==> Preparing: SELECT id,username,password FROM user
==> Parameters:
<== Columns: id, username, password
<== Row: 1, 李显赫, 1343
<== Total: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4d29be04]
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4d29be04] from current transaction
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4d29be04]
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4d29be04] from current transaction
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4d29be04]
结论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”);
总结