Since MyBatis has been integrated with spring, the MyBatis session cache is seldom used. The habit is to locally cache the local cache frame that you write with a map or introduce a third party Ehcache,guava
So put forward to tangle under
Experiment (Spring integration mybatis, online a bunch), first look at the MyBatis level session cache
Emit a print SQL statement
Configuration.xml Join
<settings> <!--print query statement-- <setting name= "Logimpl" value= "stdout_logging"/> </ Settings>
The test source code is as follows:
DAO class
/** * Test Why MyBatis in spring does not use cache * * @author He Jinbin 2017.02.15 */@Componentpublic class Testdao {private Logger Logger = L Ogger.getlogger (TestDao.class.getName ()); @Autowired private Sqlsessiontemplate sqlsessiontemplate; @Autowired private Sqlsessionfactory sqlsessionfactory; /** * two times SQL * * @param ID * @return */public testdto selectbyspring (String id) {testdto t Estdto = (testdto) sqlsessiontemplate.selectone ("Com.hejb.TestDto.selectByPrimaryKey", id); Testdto = (testdto) sqlsessiontemplate.selectone ("Com.hejb.TestDto.selectByPrimaryKey", id); return testdto; }/** * Once SQL * * @param ID * @return */public testdto Selectbymybatis (String id) {SQL Session session = Sqlsessionfactory.opensession (); Testdto testdto = Session.selectone ("Com.hejb.TestDto.selectByPrimaryKey", id); Testdto = Session.selectone ("Com.hejb.TestDto.selectByPrimaryKey", id); return testdto; }}
Testing the service class
@Componentpublic class Testservice { @Autowired private Testdao Testdao; /** * Spring mybatis query for transaction not opened * /public void Testspringcashe () { //queried two times SQL Testdao.selectbyspring ("1"); } /** * Open transaction Spring mybatis query * / @Transactional public void Testspringcashewithtran () { // After spring opens the transaction, query SQL testdao.selectbyspring ("1") 1 times; } /** * MyBatis Query * /public void Testcash4mybatise () { //original Ecology MyBatis, queried 1 times SQL Testdao.selectbymybatis ("1");} }
Output Result:
The Testspringcashe () method executes two SQL, the others are once
SOURCE Tracking:
First look at the sqlsession in MyBatis.
Trace to the last query method called to Org.apache.ibatis.executor.BaseExecutor
try { querystack++; List = Resulthandler = = null? (list<e>) Localcache.getobject (key): null; First Take if (list! = null) { handlelocallycachedoutputparameters (MS, key, parameter, Boundsql) from the cache; Note that the key inside is CacheKey } else { list = Queryfromdatabase (ms, parameter, Rowbounds, Resulthandler, Key, Boundsql); }
Paste the code that is how to remove the cached data
private void Handlelocallycachedoutputparameters (mappedstatement ms, CacheKey key, Object parameter, Boundsql boundsql) { if (ms.getstatementtype () = = statementtype.callable) { final Object cachedparameter = Localoutputparametercache.getobject (key);//Remove Cache object from Localoutputparametercache if (cachedparameter! = null & & parameter! = NULL) { final metaobject metacachedparameter = Configuration.newmetaobject (cachedparameter); Final MetaObject metaparameter = configuration.newmetaobject (parameter); For (parametermapping parameterMapping:boundSql.getParameterMappings ()) { if (Parametermapping.getmode ()! = parametermode.in) { final String parametername = Parametermapping.getproperty (); Final Object Cachedvalue = Metacachedparameter.getvalue (parametername); Metaparameter.setvalue (ParameterName, Cachedvalue);}}}}
Discovery is from Localoutputparametercache is a perpetualcache, Perpetualcache maintained a map, is the session of the cache nature.
Focus can focus on the following two tired logic
Perpetualcache, two parameters, ID and map
The Cachekey,map key, which has a overwrite Equas method, is called when the cache is fetched.
This local map cache to get the disadvantage of the object , as I stepped on the pit experience (previously I also use map to implement the local cache), is to get the object is not clone, the returned two objects are an address
In spring, the sqlsessiontemplate is generally used, as follows
<bean id= "Sqlsessionfactory" class= "Org.mybatis.spring.SqlSessionFactoryBean" > <property name= " DataSource "ref=" DataSource "/> <property name=" configlocation "value=" Classpath:configuration.xml "/> <property name= "mapperlocations" > <list> <value>classpath*:com/hejb/sqlmap/*.xml </value> </list> </property> </bean> <bean id= "Sqlsessiontemplate" class= "Org.mybatis.spring.SqlSessionTemplate" > <constructor-arg ref= "Sqlsessionfactory"/>
The session that executes SQL in Sqlsessiontemplate is all through Sqlsessionproxy, and the Sqlsessionproxy generation is assigned in the constructor, as follows:
This.sqlsessionproxy = (sqlsession) newproxyinstance ( SqlSessionFactory.class.getClassLoader (), new Class[] {Sqlsession.class}, new Sqlsessioninterceptor ());
Sqlsessionproxy a proxy class generated by the dynamic proxy method of the JDK, the main logic in the Invocationhandler to execute the method of interception before and after, the main logic in invoke, wrapped every time to execute the creation of Sqlsesstion, Common, close
The code is as follows:
Private class Sqlsessioninterceptor implements Invocationhandler {@Override public object Invoke (object proxy, Met Hod method, object[] args) throws Throwable {//Create a new sqlsession sqlsession sqlsession = getsqlsession before each execution ( SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, Sqlsessiontempl Ate.this.exceptionTranslator); try {//execute method Object result = Method.invoke (sqlsession, args); if (!issqlsessiontransactional (sqlsession, SqlSessionTemplate.this.sqlSessionFactory)) {//force commit even on Non-dirty sessions because some databases require//a commit/rollback before calling close () sqlsession . commit (TRUE); } return result; } catch (Throwable t) {Throwable unwrapped = unwrapthrowable (t); if (SqlSessionTemplate.this.exceptionTranslator! = null && unwrapped instanceof persistenceexception) {/ /RELEASE The ConnEction to avoid a deadlock if the translator is no loaded. See issue #22 Closesqlsession (sqlsession, SqlSessionTemplate.this.sqlSessionFactory); sqlsession = null; Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible (( persistenceexception) unwrapped); if (translated! = null) {unwrapped = translated; }} throw unwrapped; } finally {if (sqlsession! = null) {closesqlsession (sqlsession, SqlSessionTemplate.this.sqlSessionFactor y); } } } }
Because it is created every time, it does not use the sqlsession cache.
Why can I use it to open a transaction , follow the Getsqlsession method
As follows:
public static sqlsession getsqlsession (Sqlsessionfactory sessionfactory, Executortype Executortype, Persistenceexceptiontranslator exceptiontranslator) { notnull (sessionfactory, No_sql_session_factory_ SPECIFIED); Notnull (Executortype, no_executor_type_specified); Sqlsessionholder holder = (sqlsessionholder) transactionsynchronizationmanager.getresource (sessionFactory); First remove the session from Sqlsessionholder sqlsession session = Sessionholder (Executortype, holder); if (session! = NULL) { return session; } if (logger.isdebugenabled ()) { Logger.debug ("Creating a new sqlsession"); Session = Sessionfactory.opensession (Executortype); Registersessionholder (Sessionfactory, Executortype, Exceptiontranslator, session); return session; }
In the inside maintained a sqlsessionholder, associated with the session, if there is a direct take out, or new session, so in a transaction, each session is the same, it can be used on the cache
Leave a little thought behind
, how does CacheKey act as a key to determine whether the same SQL and parameters are executed?
Analyze why spring does not use the session cache after integrating MyBatis