spring 整合mybatis后用不上session缓存的原因分析
发布时间 - 2026-01-10 23:05:47 点击率:次因为一直用spring整合了mybatis,所以很少用到mybatis的session缓存。 习惯是本地缓存自己用map写或者引入第三方的本地缓存框架ehcache,Guava

所以提出来纠结下
实验下(spring整合mybatis略,网上一堆),先看看mybatis级别的session的缓存
放出打印sql语句
configuration.xml 加入
<settings>
<!-- 打印查询语句 -->
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>
测试源代码如下:
dao类
/**
* 测试spring里的mybatis为啥用不上缓存
*
* @author 何锦彬 2017.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.TestDto.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<E>) 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,如下
<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" />
</bean>
在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缓存的原因分析,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!
# spring
# mybatis
# 整合
# 缓存
# session
# 深入浅出重构Mybatis与Spring集成的SqlSessionFactoryBean(上)
# Spring与Mybatis基于注解整合Redis的方法
# springboot与mybatis整合实例详解(完美融合)
# AngularJS整合Springmvc、Spring、Mybatis搭建开发环境
# 浅析mybatis和spring整合的实现过程
# 都是
# 两次
# 不上
# 小编
# 好了
# 在此
# 是怎么
# 而在
# 是从
# 给大家
# 在里面
# 就用
# 每次都
# 第三方
# 所述
# 创建一个
# 先看
# 给我留言
# 也用
# 就我
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
Laravel项目怎么部署到Linux_Laravel Nginx配置详解
哪家制作企业网站好,开办像阿里巴巴那样的网络公司和网站要怎么做?
韩国网站服务器搭建指南:VPS选购、域名解析与DNS配置推荐
Laravel如何部署到服务器_线上部署Laravel项目的完整流程与步骤
EditPlus中的正则表达式 实战(2)
网站建设要注意的标准 促进网站用户好感度!
Laravel怎么使用Blade模板引擎_Laravel模板继承与Component组件复用【手册】
如何在Ubuntu系统下快速搭建WordPress个人网站?
LinuxShell函数封装方法_脚本复用设计思路【教程】
网站制作报价单模板图片,小松挖机官方网站报价?
如何在 Pandas 中基于一列条件计算另一列的分组均值
详解ASP.NET 生成二维码实例(采用ThoughtWorks.QRCode和QrCode.Net两种方式)
如何在IIS管理器中快速创建并配置网站?
如何在 Telegram Web View(iOS)中防止键盘遮挡底部输入框
javascript事件捕获机制【深入分析IE和DOM中的事件模型】
专业型网站制作公司有哪些,我设计专业的,谁给推荐几个设计师兼职类的网站?
Win11怎么更改系统语言为中文_Windows11安装语言包并设为显示语言
Laravel安装步骤详细教程_Laravel环境搭建指南
如何在万网开始建站?分步指南解析
JavaScript模板引擎Template.js使用详解
网站制作软件有哪些,制图软件有哪些?
如何在新浪SAE免费搭建个人博客?
如何在云服务器上快速搭建个人网站?
Python3.6正式版新特性预览
如何在橙子建站中快速调整背景颜色?
Win11怎么关闭专注助手 Win11关闭免打扰模式设置【操作】
深圳网站制作平台,深圳市做网站好的公司有哪些?
Mybatis 中的insertOrUpdate操作
阿里云高弹*务器配置方案|支持分布式架构与多节点部署
香港服务器租用费用高吗?如何避免常见误区?
电商网站制作多少钱一个,电子商务公司的网站制作费用计入什么科目?
Python企业级消息系统教程_KafkaRabbitMQ高并发应用
PHP的CURL方法curl_setopt()函数案例介绍(抓取网页,POST数据)
Laravel如何实现数据导出到PDF_Laravel使用snappy生成网页快照PDF【方案】
WordPress 子目录安装中正确处理脚本路径的完整指南
历史网站制作软件,华为如何找回被删除的网站?
Python自然语言搜索引擎项目教程_倒排索引查询优化案例
企业在线网站设计制作流程,想建设一个属于自己的企业网站,该如何去做?
网页设计与网站制作内容,怎样注册网站?
Windows10电脑怎么设置虚拟光驱_Win10右键装载ISO镜像文件
Laravel如何处理异常和错误?(Handler示例)
Laravel表单请求验证类怎么用_Laravel Form Request分离验证逻辑教程
如何在阿里云虚拟服务器快速搭建网站?
如何在阿里云购买域名并搭建网站?
北京企业网站设计制作公司,北京铁路集团官方网站?
Laravel如何安装Breeze扩展包_Laravel用户注册登录功能快速实现【流程】
微信小程序 闭包写法详细介绍
大连企业网站制作公司,大连2025企业社保缴费网上缴费流程?
如何续费美橙建站之星域名及服务?
如何在万网主机上快速搭建网站?

