解决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时一级缓存失效的问题就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持毛票票。