mybatis 的一级缓存
简单回顾下mybatis的一级缓存
本质上是一个基于map实现的内存级别的缓存,默认开启,生命周期是 sqlsession 级别的
为什么会失效
其实这个问题反向分析一下就会有思路了,一级缓存默认是sqlsession级别的,这个规则不变的情况下
既然spring 中 mybatis 缓存失效了,那么就说嘛在执行sql的时候用了不同的sqlsession
源码解析
先简单回顾下spring 是如何整和mybatis的
a.单独为mybatis写一个扫描器,并重写扫描器的扫描规则,允许扫描到接口
b.将扫描的接口对应到一个个 MapperFactoryBean
c.MapperFactoryBean 是一个 FactoryBean,那么在实际获取Mapper的时候就会调用MapperFactoryBean.getObject ()
MapperFactoryBean.getObject ()通过 mybatis 代理生成对象
很明显这个问题的入口在 MapperFactoryBean.getObject ()
MapperFactoryBean
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
public abstract class SqlSessionDaoSupport extends DaoSupport {
private SqlSession sqlSession;
private boolean externalSqlSession;
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
if (!this.externalSqlSession) {
this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
}
}
public SqlSession getSqlSession() {
return this.sqlSession;
}
.....
}
可以看到最终返回的 SqlSession 并不是 mybaits 的 SqlSession,而是SqlSessionTemplate
public class SqlSessionTemplate implements SqlSession, DisposableBean {
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
notNull(executorType, "Property 'executorType' is required");
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
this.sqlSessionProxy = (SqlSession) newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class },
new SqlSessionInterceptor());
}
....
}
SqlSessionTemplate其实就是对 SqlSession 进行了一层代理
在增强逻辑里finally 中执行了closeSqlsession方法,这一下就很明了了
不出意外的话就是每个sql开启一个sqlsession
public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
//申明式事物
if ((holder != null) && (holder.getSqlSession() == session)) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Releasing transactional SqlSession [" + session + "]");
}
holder.released();
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Closing non transactional SqlSession [" + session + "]");
}
session.close();
}
}
很显然,在没有使用申明式实物的情况下,每次执行完sql都会进行sqlsession的回收
总结
在非申明式事物场景下,spring 中使用 mybatis 一级缓存失效的原因是因为spring并没有直接使用
sqlSession ,而是使用了 SqlSessionTemplate,通过 SqlSessionTemplate 来进行 sqlSession 的使用
但是在 SqlSessionTemplate 中,每次都会申请新的 sqlSession ,从而导致一级缓存失效