spring 整合mybatis后用不上session缓存的原因分析

网友投稿 249 2023-06-13


spring 整合mybatis后用不上session缓存的原因分析

因为一直用spring整合了mybatis,所以很少用到mybatis的session缓存。 习惯是本地缓存自己用map写或者引入第三方的本地缓存框架ehcache,Guava

所以提出来纠结下

实验下(spring整合mybatis略,网上一堆),先看看mybatis级别的session的缓存

放出打印sql语句

configuration.xml 加入

测试源代码如下:

dao类

/**

* 测试spring里的mybatis为啥用不上缓存

*

* @author 何锦彬 2BMmbNwrfO017.02.15

*/

@Component

public class TestDao {

private Logger logger = Logger.getLogger(TestDao.class.getName());

@Autowired

private SqlSessionTemplate sqlSessionTemplate;

@Autowired

private SqlSessionFactory sqlSessionFactory;

/**

* 两次SQL

*

* @param id

* @return

*/

public TestDto selectBySpring(String id) {

TestDto testDto = (TestDto) sqlSessionTemplate.selectOne("com.hejb.TestDtoBMmbNwrfO.selectByPrimaryKey", id);

testDto = (TestDto) sqlSessionTemplate.selectOne("com.hejb.TestDto.selectByPrimaryKey", id);

return testDto;

}

/**

* 一次SQL

*

* @param id

* @return

*/

public TestDto selectByMybatis(String id) {

SqlSession session = sqlSessionFactory.openSession();

TestDto testDto = session.selectOne("com.hejb.TestDto.selectByPrimaryKey", id);

testDto = session.selectOne("com.hejb.TestDto.selectByPrimaryKey", id);

return testDto;

}

}

测试service类

@Component

public class TestService {

@Autowired

private TestDao testDao;

/**

* 未开启事务的spring Mybatis查询

*/

public void testSpringCashe() {

//查询了两次SQL

testDao.selectBySpring("1");

}

/**

* 开启事务的spring Mybatis查询

*/

@Transactional

public void testSpringCasheWithTran() {

//spring开启事务后,查询1次SQL

testDao.selectBySpring("1");

}

/**

* mybatis查询

*/

public void testCash4Mybatise() {

//原生态mybatis,查询了1次SQL

testDao.selectByMybatis("1");

}

}

输出结果:

testSpringCashe()方法执行了两次SQL, 其它都是一次

源码追踪:

先看mybatis里的sqlSession

跟踪到最后 调用到 org.apache.ibatis.executor.BaseExecutor的query方法

try {

queryStack++;

list = resultHandler == null ? (List) localCache.getObject(key) : null; //先从缓存中取

if (list != null) {

handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); //注意里面的key是CacheKey

} else {

list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);

}

贴下是怎么取出缓存数据的代码

private void handleLocallyCachedOutputParameters(MappedStatement ms, CacheKey key, Object parameter, BoundSql boundSql) {

if (ms.getStatementType() == StatementType.CALLABLE) {

final Object cachedParameter = localOutputParameterCache.getObject(key);//从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);

}

}

}

}

}

发现就是从localOutputParameterCache就是一个PerpetualCache, PerpetualCache维护了个map,就是session的缓存本质了。

重点可以关注下面两个累的逻辑

PerpetualCache , 两个参数, id和map

CacheKey,map中存的key,它有覆盖equas方法,当获取缓存时调用.

这种本地map缓存获取对象的缺点,就我踩坑经验(以前我也用map去实现的本地缓存),就是获取的对象非clone的,返回的两个对象都是一个地址

而在spring中一般都是用sqlSessionTemplate,如下

classpath*:com/hejb/sqlmap/*.xml

在SqlSessionTemplate中执行SQL的session都是通过sqlSessionProxy来,sqlSessionProxy的生成在构造函数中赋值,如下:

this.sqlSessionProxy = (SqlSession) newProxyInstance(

SqlSessionFactory.class.getClassLoader(),

new Class[] { SqlSession.class },

new SqlSessionInterceptor());

sqlSessionProxy通过JDK的动态代理方法生成的一个代理类,主要逻辑在InvocationHandler对执行的方法进行了前后拦截,主要逻辑在invoke中,包好了每次执行对sqlsesstion的创建,common,关闭

代码如下:

private class SqlSessionInterceptor implements InvocationHandler {

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

// 每次执行前都创建一个新的sqlSession

SqlSession sqlSession = getSqlSession(

SqlSessionTemplate.this.sqlSessionFactory,

SqlSessionTemplate.this.executorType,

SqlSessionTemplate.this.exceptionTranslator);

try {

// 执行方法

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.sqlSessionFactory);

}

}

}

}

因为每次都进行创建,所以就用不上sqlSession的缓存了.

对于开启了事务为什么可以用上呢, 跟入getSqlSession方法

如下:

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);

// 首先从SqlSessionHolder里取出session

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;

}

在里面维护了个SqlSessionHolder,关联了事务与session,如果存在则直接取出,否则则新建个session,所以在有事务的里,每个session都是同一个,故能用上缓存了

以上所述是给大家介绍的spring 整合mybatis后用不上session缓存的原因分析,希望对大家有所帮助,如果大家有任何疑问请给我留言,会及时回复大家的。在此也非常感谢大家对我们网站的支持!


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:java String类常用方法练习小结
下一篇:jq给页面添加覆盖层遮罩的实例
相关文章

 发表评论

暂时没有评论,来抢沙发吧~