解决spring结合mybatis时一级缓存失效的问题
之前了解到mybatis的一级缓存是默认开启的,作用域是sqlSession,是基HashMap的本地缓存。不同的SqlSession之间的缓存数据区域互不影响。
当进行select、update、delete操作后并且commit事物到数据库之后,sqlSession中的Cache自动被清空
结论
spring结合mybatis后,一级缓存作用:
在未开启事物的情况之下,每次查询,spring都会关闭旧的sqlSession而创建新的sqlSession,因此此时的一级缓存是没有启作用的
在开启事物的情况之下,spring使用threadLocal获取当前资源绑定同一个sqlSession,因此此时一级缓存是有效的
案例
情景一:未开启事物
@Service("countryService")
publicclassCountryService{
@Autowired
privateCountryDaocountryDao;
//@Transactional未开启事物
publicvoidnoTranSactionMethod()throwsJsonProcessingException{
CountryDocountryDo=countryDao.getById(1L);
CountryDocountryDo1=countryDao.getById(1L);
ObjectMapperobjectMapper=newObjectMapper();
Stringjson=objectMapper.writeValueAsString(countryDo);
Stringjson1=objectMapper.writeValueAsString(countryDo1);
System.out.println(json);
System.out.println(json1);
}
}
测试案例:
@Test
publicvoidtransactionTest()throwsJsonProcessingException{
countryService.noTranSactionMethod();
}
结果:
[DEBUG]SqlSessionUtilsCreatinganewSqlSession
[DEBUG]SpringManagedTransactionJDBCConnection[com.mysql.jdbc.JDBC4Connection@14a54ef6]willnotbemanagedbySpring
[DEBUG]getById==>Preparing:SELECT*FROMcountryWHEREcountry_id=?
[DEBUG]getById==>Parameters:1(Long)
[DEBUG]getById<==Total:1
[DEBUG]SqlSessionUtilsClosingnontransactionalSqlSession[org.apache.ibatis.session.defaults.DefaultSqlSession@3359c978]
[DEBUG]SqlSessionUtilsCreatinganewSqlSession
[DEBUG]SqlSessionUtilsSqlSession[org.apache.ibatis.session.defaults.DefaultSqlSession@2aa27288]wasnotregisteredforsynchronizationbecausesynchronizationisnotactive
[DEBUG]SpringManagedTransactionJDBCConnection[com.mysql.jdbc.JDBC4Connection@14a54ef6]willnotbemanagedbySpring
[DEBUG]getById==>Preparing:SELECT*FROMcountryWHEREcountry_id=?
[DEBUG]getById==>Parameters:1(Long)
[DEBUG]getById<==Total:1
[DEBUG]SqlSessionUtilsClosingnontransactionalSqlSession[org.apache.ibatis.session.defaults.DefaultSqlSession@2aa27288]
{"countryId":1,"country":"Afghanistan","lastUpdate":"2006-02-1504:44:00.0"}
{"countryId":1,"country":"Afghanistan","lastUpdate":"2006-02-1504:44:00.0"}
可以看到,两次查询,都创建了新的sqlSession,并向数据库查询,此时缓存并没有起效果
情景二:开启事物
打开@Transactional注解:
@Service("countryService")
publicclassCountryService{
@Autowired
privateCountryDaocountryDao;
@Transactional
publicvoidnoTranSactionMethod()throwsJsonProcessingException{
CountryDocountryDo=countryDao.getById(1L);
CountryDocountryDo1=countryDao.getById(1L);
ObjectMapperobjectMapper=newObjectMapper();
Stringjson=objectMapper.writeValueAsString(countryDo);
Stringjson1=objectMapper.writeValueAsString(countryDo1);
System.out.println(json);
System.out.println(json1);
}
}
使用原来的测试案例,输出结果:
[DEBUG]SqlSessionUtilsCreatinganewSqlSession
[DEBUG]SqlSessionUtilsRegisteringtransactionsynchronizationforSqlSession[org.apache.ibatis.session.defaults.DefaultSqlSession@109f5dd8]
[DEBUG]SpringManagedTransactionJDBCConnection[com.mysql.jdbc.JDBC4Connection@55caeb35]willbemanagedbySpring
[DEBUG]getById==>Preparing:SELECT*FROMcountryWHEREcountry_id=?
[DEBUG]getById==>Parameters:1(Long)
[DEBUG]getById<==Total:1
[DEBUG]SqlSessionUtilsReleasingtransactionalSqlSession[org.apache.ibatis.session.defaults.DefaultSqlSession@109f5dd8]
//从当前事物中获取sqlSession
[DEBUG]SqlSessionUtilsFetchedSqlSession[org.apache.ibatis.session.defaults.DefaultSqlSession@109f5dd8]fromcurrenttransaction
[DEBUG]SqlSessionUtilsReleasingtransactionalSqlSession[org.apache.ibatis.session.defaults.DefaultSqlSession@109f5dd8]
{"countryId":1,"country":"Afghanistan","lastUpdate":"2006-02-1504:44:00.0"}
{"countryId":1,"country":"Afghanistan","lastUpdate":"2006-02-1504:44:00.0"}
可以看到,两次查询,只创建了一次sqlSession,说明一级缓存起作用了
跟踪源码
从SqlSessionDaoSupport作为路口,这个类在mybatis-spring包下,sping为sqlSession做了代理
publicabstractclassSqlSessionDaoSupportextendsDaoSupport{
privateSqlSessionsqlSession;
privatebooleanexternalSqlSession;
publicvoidsetSqlSessionFactory(SqlSessionFactorysqlSessionFactory){
if(!this.externalSqlSession){
this.sqlSession=newSqlSessionTemplate(sqlSessionFactory);
}
}
//....omit
}
创建了SqlSessionTemplate后,在SqlSessionTemplate中:
publicSqlSessionTemplate(SqlSessionFactorysqlSessionFactory,ExecutorTypeexecutorType,
PersistenceExceptionTranslatorexceptionTranslator){
notNull(sqlSessionFactory,"Property'sqlSessionFactory'isrequired");
notNull(executorType,"Property'executorType'isrequired");
this.sqlSessionFactory=sqlSessionFactory;
this.executorType=executorType;
this.exceptionTranslator=exceptionTranslator;
//代理了SqlSession
this.sqlSessionProxy=(SqlSession)newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
newClass[]{SqlSession.class},
newSqlSessionInterceptor());
}
再看SqlSessionInterceptor,SqlSessionInterceptor是SqlSessionTemplate的内部类:
publicclassSqlSessionTemplateimplementsSqlSession,DisposableBean{
//...omit..
privateclassSqlSessionInterceptorimplementsInvocationHandler{
@Override
publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{
SqlSessionsqlSession=getSqlSession(
SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType,
SqlSessionTemplate.this.exceptionTranslator);
try{
Objectresult=method.invoke(sqlSession,args);
//如果尚未开启事物(事物不是由spring来管理),则sqlSession直接提交
if(!isSqlSessionTransactional(sqlSession,SqlSessionTemplate.this.sqlSessionFactory)){
//forcecommitevenonnon-dirtysessionsbecausesomedatabasesrequire
//acommit/rollbackbeforecallingclose()
//手动commit
sqlSession.commit(true);
}
returnresult;
}catch(Throwablet){
Throwableunwrapped=unwrapThrowable(t);
if(SqlSessionTemplate.this.exceptionTranslator!=null&&unwrappedinstanceofPersistenceException){
//releasetheconnectiontoavoidadeadlockifthetranslatorisnoloaded.Seeissue#22
closeSqlSession(sqlSession,SqlSessionTemplate.this.sqlSessionFactory);
sqlSession=null;
Throwabletranslated=SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped);
if(translated!=null){
unwrapped=translated;
}
}
throwunwrapped;
}finally{
//一般情况下,默认都是关闭sqlSession
if(sqlSession!=null){
closeSqlSession(sqlSession,SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
}
再看getSqlSession方法,这个方法是在SqlSessionUtils.java中的:
publicstaticSqlSessiongetSqlSession(SqlSessionFactorysessionFactory,ExecutorTypeexecutorType,PersistenceExceptionTranslatorexceptionTranslator){
notNull(sessionFactory,NO_SQL_SESSION_FACTORY_SPECIFIED);
notNull(executorType,NO_EXECUTOR_TYPE_SPECIFIED);
//获取holder
SqlSessionHolderholder=(SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
//从sessionHolder中获取SqlSession
SqlSessionsession=sessionHolder(executorType,holder);
if(session!=null){
returnsession;
}
if(LOGGER.isDebugEnabled()){
LOGGER.debug("CreatinganewSqlSession");
}
//如果sqlSession不存在,则创建一个新的
session=sessionFactory.openSession(executorType);
//将sqlSession注册在sessionHolder中
registerSessionHolder(sessionFactory,executorType,exceptionTranslator,session);
returnsession;
}
privatestaticvoidregisterSessionHolder(SqlSessionFactorysessionFactory,ExecutorTypeexecutorType,
PersistenceExceptionTranslatorexceptionTranslator,SqlSessionsession){
SqlSessionHolderholder;
//在开启事物的情况下
if(TransactionSynchronizationManager.isSynchronizationActive()){
Environmentenvironment=sessionFactory.getConfiguration().getEnvironment();
//由spring来管理事物的情况下
if(environment.getTransactionFactory()instanceofSpringManagedTransactionFactory){
if(LOGGER.isDebugEnabled()){
LOGGER.debug("RegisteringtransactionsynchronizationforSqlSession["+session+"]");
}
holder=newSqlSessionHolder(session,executorType,exceptionTranslator);
//将sessionFactory绑定在sessionHolde相互绑定
TransactionSynchronizationManager.bindResource(sessionFactory,holder);
TransactionSynchronizationManager.registerSynchronization(newSqlSessionSynchronization(holder,sessionFactory));
holder.setSynchronizedWithTransaction(true);
holder.requested();
}else{
if(TransactionSynchronizationManager.getResource(environment.getDataSource())==null){
if(LOGGER.isDebugEnabled()){
LOGGER.debug("SqlSession["+session+"]wasnotregisteredforsynchronizationbecauseDataSourceisnottransactional");
}
}else{
thrownewTransientDataAccessResourceException(
"SqlSessionFactorymustbeusingaSpringManagedTransactionFactoryinordertouseSpringtransactionsynchronization");
}
}
}else{
if(LOGGER.isDebugEnabled()){
LOGGER.debug("SqlSession["+session+"]wasnotregisteredforsynchronizationbecausesynchronizationisnotactive");
}
}
再看TransactionSynchronizationManager.bindResource的方法:
publicabstractclassTransactionSynchronizationManager{
//omit...
privatestaticfinalThreadLocal
这里可以看到,spring是如何做到获取到的是同一个SqlSession,前面的长篇大论,就是为使用ThreadLocal将当前线程绑定创建SqlSession相关的资源,从而获取同一个sqlSession
以上这篇解决spring结合mybatis时一级缓存失效的问题就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持毛票票。