解决spring结合mybatis时一级缓存失效的问题
之前了解到mybatis的一级缓存是默认开启的,作用域是sqlsession,是基 hashmap的本地缓存。不同的sqlsession之间的缓存数据区域互不影响。
当进行select、update、delete操作后并且commit事物到数据库之后,sqlsession中的cache自动被清空
<setting name="localcachescope" value="session"/>
结论
spring结合mybatis后,一级缓存作用:
在未开启事物的情况之下,每次查询,spring都会关闭旧的sqlsession而创建新的sqlsession,因此此时的一级缓存是没有启作用的
在开启事物的情况之下,spring使用threadlocal获取当前资源绑定同一个sqlsession,因此此时一级缓存是有效的
案例
情景一:未开启事物
@service("countryservice") public class countryservice { @autowired private countrydao countrydao; // @transactional 未开启事物 public void notransactionmethod() throws jsonprocessingexception { countrydo countrydo = countrydao.getbyid(1l); countrydo countrydo1 = countrydao.getbyid(1l); objectmapper objectmapper = new objectmapper(); string json = objectmapper.writevalueasstring(countrydo); string json1 = objectmapper.writevalueasstring(countrydo1); system.out.println(json); system.out.println(json1); } }
测试案例:
@test public void transactiontest() throws jsonprocessingexception { countryservice.notransactionmethod(); }
结果:
[debug] sqlsessionutils creating a new sqlsession [debug] springmanagedtransaction jdbc connection [com.mysql.jdbc.jdbc4connection@14a54ef6] will not be managed by spring [debug] getbyid ==> preparing: select * from country where country_id = ? [debug] getbyid ==> parameters: 1(long) [debug] getbyid <== total: 1 [debug] sqlsessionutils closing non transactional sqlsession [org.apache.ibatis.session.defaults.defaultsqlsession@3359c978] [debug] sqlsessionutils creating a new sqlsession [debug] sqlsessionutils sqlsession [org.apache.ibatis.session.defaults.defaultsqlsession@2aa27288] was not registered for synchronization because synchronization is not active [debug] springmanagedtransaction jdbc connection [com.mysql.jdbc.jdbc4connection@14a54ef6] will not be managed by spring [debug] getbyid ==> preparing: select * from country where country_id = ? [debug] getbyid ==> parameters: 1(long) [debug] getbyid <== total: 1 [debug] sqlsessionutils closing non transactional sqlsession [org.apache.ibatis.session.defaults.defaultsqlsession@2aa27288] {"countryid":1,"country":"afghanistan","lastupdate":"2006-02-15 04:44:00.0"} {"countryid":1,"country":"afghanistan","lastupdate":"2006-02-15 04:44:00.0"}
可以看到,两次查询,都创建了新的sqlsession,并向数据库查询,此时缓存并没有起效果
情景二: 开启事物
打开@transactional注解:
@service("countryservice") public class countryservice { @autowired private countrydao countrydao; @transactional public void notransactionmethod() throws jsonprocessingexception { countrydo countrydo = countrydao.getbyid(1l); countrydo countrydo1 = countrydao.getbyid(1l); objectmapper objectmapper = new objectmapper(); string json = objectmapper.writevalueasstring(countrydo); string json1 = objectmapper.writevalueasstring(countrydo1); system.out.println(json); system.out.println(json1); } }
使用原来的测试案例,输出结果:
[debug] sqlsessionutils creating a new sqlsession [debug] sqlsessionutils registering transaction synchronization for sqlsession [org.apache.ibatis.session.defaults.defaultsqlsession@109f5dd8] [debug] springmanagedtransaction jdbc connection [com.mysql.jdbc.jdbc4connection@55caeb35] will be managed by spring [debug] getbyid ==> preparing: select * from country where country_id = ? [debug] getbyid ==> parameters: 1(long) [debug] getbyid <== total: 1 [debug] sqlsessionutils releasing transactional sqlsession [org.apache.ibatis.session.defaults.defaultsqlsession@109f5dd8] // 从当前事物中获取sqlsession [debug] sqlsessionutils fetched sqlsession [org.apache.ibatis.session.defaults.defaultsqlsession@109f5dd8] from current transaction [debug] sqlsessionutils releasing transactional sqlsession [org.apache.ibatis.session.defaults.defaultsqlsession@109f5dd8] {"countryid":1,"country":"afghanistan","lastupdate":"2006-02-15 04:44:00.0"} {"countryid":1,"country":"afghanistan","lastupdate":"2006-02-15 04:44:00.0"}
可以看到,两次查询,只创建了一次sqlsession,说明一级缓存起作用了
跟踪源码
从sqlsessiondaosupport作为路口,这个类在mybatis-spring包下,sping为sqlsession做了代理
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); } } //....omit }
创建了sqlsessiontemplate后,在sqlsessiontemplate中:
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; //代理了sqlsession this.sqlsessionproxy = (sqlsession) newproxyinstance( sqlsessionfactory.class.getclassloader(), new class[] { sqlsession.class }, new sqlsessioninterceptor()); }
再看sqlsessioninterceptor,sqlsessioninterceptor是sqlsessiontemplate的内部类:
public class sqlsessiontemplate implements sqlsession, disposablebean { // ...omit.. private class sqlsessioninterceptor implements invocationhandler { @override public object invoke(object proxy, method method, object[] args) throws throwable { sqlsession sqlsession = getsqlsession( sqlsessiontemplate.this.sqlsessionfactory, sqlsessiontemplate.this.executortype, sqlsessiontemplate.this.exceptiontranslator); try { object result = method.invoke(sqlsession, args); //如果尚未开启事物(事物不是由spring来管理),则sqlsession直接提交 if (!issqlsessiontransactional(sqlsession, sqlsessiontemplate.this.sqlsessionfactory)) { // force commit even on non-dirty sessions because some databases require // a commit/rollback before calling close() // 手动commit 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 { //一般情况下,默认都是关闭sqlsession if (sqlsession != null) { closesqlsession(sqlsession, sqlsessiontemplate.this.sqlsessionfactory); } } } } }
再看getsqlsession方法,这个方法是在sqlsessionutils.java中的:
public static sqlsession getsqlsession(sqlsessionfactory sessionfactory, executortype executortype, persistenceexceptiontranslator exceptiontranslator) { notnull(sessionfactory, no_sql_session_factory_specified); notnull(executortype, no_executor_type_specified); //获取holder sqlsessionholder holder = (sqlsessionholder) transactionsynchronizationmanager.getresource(sessionfactory); //从sessionholder中获取sqlsession sqlsession session = sessionholder(executortype, holder); if (session != null) { return session; } if (logger.isdebugenabled()) { logger.debug("creating a new sqlsession"); } //如果sqlsession不存在,则创建一个新的 session = sessionfactory.opensession(executortype); //将sqlsession注册在sessionholder中 registersessionholder(sessionfactory, executortype, exceptiontranslator, session); return session; } private static void registersessionholder(sqlsessionfactory sessionfactory, executortype executortype, persistenceexceptiontranslator exceptiontranslator, sqlsession session) { sqlsessionholder holder; //在开启事物的情况下 if (transactionsynchronizationmanager.issynchronizationactive()) { environment environment = sessionfactory.getconfiguration().getenvironment(); //由spring来管理事物的情况下 if (environment.gettransactionfactory() instanceof springmanagedtransactionfactory) { if (logger.isdebugenabled()) { logger.debug("registering transaction synchronization for sqlsession [" + session + "]"); } holder = new sqlsessionholder(session, executortype, exceptiontranslator); //将sessionfactory绑定在sessionholde相互绑定 transactionsynchronizationmanager.bindresource(sessionfactory, holder); transactionsynchronizationmanager.registersynchronization(new sqlsessionsynchronization(holder, sessionfactory)); holder.setsynchronizedwithtransaction(true); holder.requested(); } else { if (transactionsynchronizationmanager.getresource(environment.getdatasource()) == null) { if (logger.isdebugenabled()) { logger.debug("sqlsession [" + session + "] was not registered for synchronization because datasource is not transactional"); } } else { throw new transientdataaccessresourceexception( "sqlsessionfactory must be using a springmanagedtransactionfactory in order to use spring transaction synchronization"); } } } else { if (logger.isdebugenabled()) { logger.debug("sqlsession [" + session + "] was not registered for synchronization because synchronization is not active"); } }
再看transactionsynchronizationmanager.bindresource的方法:
public abstract class transactionsynchronizationmanager { //omit... private static final threadlocal<map<object, object>> resources = new namedthreadlocal<map<object, object>>("transactional resources"); // key:sessionfactory, value:sqlsessionholder(connection) public static void bindresource(object key, object value) throws illegalstateexception { object actualkey = transactionsynchronizationutils.unwrapresourceifnecessary(key); assert.notnull(value, "value must not be null"); //从threadlocal类型的resources中获取与当前线程绑定的资源,如sessionfactory,connection等等 map<object, object> map = resources.get(); // set threadlocal map if none found if (map == null) { map = new hashmap<object, object>(); resources.set(map); } object oldvalue = map.put(actualkey, value); // transparently suppress a resourceholder that was marked as void... if (oldvalue instanceof resourceholder && ((resourceholder) oldvalue).isvoid()) { oldvalue = null; } if (oldvalue != null) { throw new illegalstateexception("already value [" + oldvalue + "] for key [" + actualkey + "] bound to thread [" + thread.currentthread().getname() + "]"); } if (logger.istraceenabled()) { logger.trace("bound value [" + value + "] for key [" + actualkey + "] to thread [" + thread.currentthread().getname() + "]"); } } }
这里可以看到,spring是如何做到获取到的是同一个sqlsession,前面的长篇大论,就是为使用threadlocal将当前线程绑定创建sqlsession相关的资源,从而获取同一个sqlsession
以上这篇解决spring结合mybatis时一级缓存失效的问题就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。
推荐阅读
-
解决spring结合mybatis时一级缓存失效的问题
-
PHP使用Memcache时模拟命名空间及缓存失效问题的解决_PHP
-
PHP使用Memcache时模拟命名空间及缓存失效问题的解决_php实例
-
PHP使用Memcache时模拟命名空间及缓存失效问题的解决
-
PHP使用Memcache时模拟命名空间及缓存失效问题的解决_PHP
-
PHP使用Memcache时模拟命名空间及缓存失效问题的解决,memcache命名空间_PHP教程
-
PHP使用Memcache时模拟命名空间及缓存失效问题的解决
-
PHP使用Memcache时模拟命名空间及缓存失效问题的解决,memcache命名空间
-
PHP使用Memcache时模拟命名空间及缓存失效问题的解决,memcache命名空间
-
解决spring结合mybatis时一级缓存失效的问题