Analyze why spring does not use the session cache after integrating MyBatis

Source: Internet
Author: User
Tags throwable

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

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.