Mybatis 的 SqlSession 和一级缓存为什么失效?

SqlSession是什么

SqlSession是Mybatis 中定义的,用来表示与关系数据库的一次会话,会话定义了各种具体的操作,查询、数据更新(包含保存、更新、删除)操作。而这些操作都在与数据库建立会话的基础上进行的。SqlSession 可以看作是对Connection 更加高级的抽象,从其方法上更加可以看出他具有更加明显的操作特征。

SqlSession分类

mybatis的SqlSession有三种:DefaultSqlSession、SqlSessionManager、SqlSessionTemplate,前两者是 mybtais 默认情况下使用的,第三种主要用到 mybatis 和 spring 整合的时候。

SqlSession的创建

SqlSessionFactory.openSession() 的方法源码

// org/apache/ibatis/session/defaults/DefaultSqlSessionFactory.java
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}

通过源码我们知道每次 SqlSession(准确地说是 DefaultSqlSession )的创建都会有一个 Transaction 事务对象 的生成。也就是说:

  • 一个事务 Transaction 对象与一个 SqlSession 对象是一对一的关系

  • 同一个 SqlSession 不管执行多少次数据库操作,只要没有执行 close,那么,整个操作都是在同一个 Transaction 中执行的

但需要注意的是,我们整合Spring之后用到的其实都是 SqlSessionTemplate ,与这里的 DefaultSqlSession 不是同一个SqlSession对象,不懂的看上面的。

为什么和 Spring 整合后的 SqlSession 一级缓存偶尔会失效

一级缓存和二级缓存

一级缓存是SqlSession级别的缓存,在操作数据库时,每个SqlSession类的实例对象缓存的数据区域(Map)可以用于存储缓存数据,不同的SqlSession类的实例对象缓存的数据区域是互不影响的。一级缓存工作原理图:

image

二级缓存是 Mapper 级别的缓存,多个 SqlSession 实例对象可以共用二级缓存,二级缓存是跨 SqlSession 的。 二级缓存模式图如下:

image

我们都知道一级缓存是 SqlSession 级别的缓存,那么,一级缓存失效,肯定是因为 SqlSession 不一致,那么,我们进入到 getSqlSession 方法中:

// org/mybatis/spring/SqlSessionUtils.java
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
// ...
// 从ThreadLocal变量里面获取到Spring的事务同步管理器
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
// 调用静态方法 sessionHoler 判断是否存在符合要求的sqlSession
SqlSession session = sessionHolder(executorType, holder);
if (session != null) {
return session;
}
// 如果 SqlSessionHolder 中获取的 SqlSession 为空,则新建一个 SqlSession
session = sessionFactory.openSession(executorType);
// 判断当前是否存在事务,将sqlSession 绑定到 sqlSessionHolder 中,并放到 threadLoacl 当中
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
return session;
}

可以看出,Spring 只有在开启了事务之后,在同一个事务里的 SqlSession 会被缓存起来,同一个事务中,多次查询是可以命中缓存的。

总结

  • 同一事务中不管调用多少次 mapper 里的方法 ,最终,都是用的同一个 sqlSession,即一个事务中使用的是同一个sqlSession

  • 同一事务中,Mybatis 的一级缓存才会有效

  • 如果没有开启事务,调用一次 mapper 里的方法将会新建一个sqlSession来执行方法


本文作者:LARRY1024

本文链接:https://www.cnblogs.com/larry1024/p/18007644

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   LARRY1024  阅读(77)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.