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类的实例对象缓存的数据区域是互不影响的。一级缓存工作原理图:
二级缓存是 Mapper 级别的缓存,多个 SqlSession 实例对象可以共用二级缓存,二级缓存是跨 SqlSession 的。 二级缓存模式图如下:
我们都知道一级缓存是 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 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步