mybatis源码分析(3)-----SqlSessionHolder作用
1、 sqlSessionHolder 是位于mybatis-spring 包下面,他的作用是对于sqlSession和事务的控制

- sqlSessionHolder 继承了spring的ResourceHolderSupport
public abstract class ResourceHolderSupport implements ResourceHolder {
//事务是否开启
private boolean synchronizedWithTransaction = false;
private boolean rollbackOnly = false;
private Date deadline;
// 引用次数
private int referenceCount = 0;
private boolean isVoid = false;
}
2 、在前面讲解到,sqlSessionTemplate 操作数据库实际操作是对于代理对象 目标方法的执行。
- 代理对象是如何获取defaultSqlSession ,在代理方法中通过SqlSessionUtils 的方法获取SqlSession
- 它会首先获取SqlSessionHolder,SqlSessionHolder用于在TransactionSynchronizationManager中保持当前的SqlSession。
- 如果holder不为空,并且holder被事务锁定,则可以通过holder.getSqlSession()方法,从当前事务中获取sqlSession,即 Fetched SqlSession from current transaction。
- 如果不存在holder或没有被事务锁定,则会创建新的sqlSession,即 Creating a new SqlSession,通过sessionFactory.openSession()方法。
- 如果当前线程的事务是活跃的,将会为SqlSession注册事务同步,即 Registering transaction synchronization for SqlSession。
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
//从从前线程的threadLocal 中获取sqlSessionHolder
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
//调用静态方法sessionHoler 判断是否存在符合要求的sqlSession
SqlSession session = sessionHolder(executorType, holder);
// 判断当前sqlSessionHolder 中是否持有sqlSession (即当前操作是否在事务当中)
if (session != null) {
//如果持有sqlSesison 的引用,则直接获取
return session;
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Creating a new SqlSession");
}
//获取新的sqlSession 对象。这里由sessionFacory产生的defaultSqlSession
session = sessionFactory.openSession(executorType);
//判断判断,当前是否存在事务,将sqlSession 绑定到sqlSessionHolder 中,并放到threadLoacl 当中
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
return session;
}
private static SqlSession sessionHolder(ExecutorType executorType, SqlSessionHolder holder) {
SqlSession session = null;
if (holder != null && holder.isSynchronizedWithTransaction()) {
//hodler保存的执行类型和获取SqlSession的执行类型不一致,就会抛出异常,也就是说在同一个事务中,执行类型不能变化,原因就是同一个事务中同一个sqlSessionFactory创建的sqlSession会被重用
if (holder.getExecutorType() != executorType) {
throw new TransientDataAccessResourceException("Cannot change the ExecutorType when there is an existing transaction");
}
//增加该holder,也就是同一事务中同一个sqlSessionFactory创建的唯一sqlSession,其引用数增加,被使用的次数增加
holder.requested(); if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Fetched SqlSession [" + holder.getSqlSession() + "] from current transaction");
}
//返回sqlSession
session = holder.getSqlSession();
}
return session;
}- 注册的方法如下
private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator, SqlSession session) {
SqlSessionHolder holder;
//判断事务是否存在
if (TransactionSynchronizationManager.isSynchronizationActive()) {
Environment environment = sessionFactory.getConfiguration().getEnvironment();
//加载环境变量,判断注册的事务管理器是否是SpringManagedTransaction,也就是Spring管理事务
if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registering transaction synchronization for SqlSession [" + session + "]");
} holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
//如果当前回话处在事务当中,则将holder 绑定到ThreadLocal 中
//以sessionFactory为key,hodler为value,加入到TransactionSynchronizationManager管理的本地缓存ThreadLocal<Map<Object, Object>> resources中
TransactionSynchronizationManager.bindResource(sessionFactory, holder);
//将holder, sessionFactory的同步加入本地线程缓存中ThreadLocal<Set<TransactionSynchronization>> synchronizations
TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
//设置当前holder和当前事务同步
holder.setSynchronizedWithTransaction(true);
//holder 引用次数+1
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");
}
}
}
4. 在sqlSession 关闭session 的时候, 使用了工具了sqlSessionUtils的closeSqlSession 方法。sqlSessionHolder 也是做了判断,如果回话在事务当中,则减少引用次数,没有真实关闭session。如果回话不存在事务,则直接关闭session
public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
notNull(session, NO_SQL_SESSION_SPECIFIED);
notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
//如果holder 中持有sqlSession 的引用,(即会话存在事务)
if ((holder != null) && (holder.getSqlSession() == session)) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Releasing transactional SqlSession [" + session + "]");
}
//每当一个sqlSession 执行完毕,则减少holder 持有引用的次数
holder.released();
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Closing non transactional SqlSession [" + session + "]");
}
//如果回话中,不存在事务,则直接关闭session
session.close();
}
}
mybatis源码分析(3)-----SqlSessionHolder作用的更多相关文章
- 精尽MyBatis源码分析 - MyBatis-Spring 源码分析
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...
- MyBatis源码分析-MyBatis初始化流程
MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简 ...
- MyBatis源码分析(3)—— Cache接口以及实现
@(MyBatis)[Cache] MyBatis源码分析--Cache接口以及实现 Cache接口 MyBatis中的Cache以SPI实现,给需要集成其它Cache或者自定义Cache提供了接口. ...
- 【MyBatis源码分析】select源码分析及小结
示例代码 之前的文章说过,对于MyBatis来说insert.update.delete是一组的,因为对于MyBatis来说它们都是update:select是一组的,因为对于MyBatis来说它就是 ...
- MyBatis源码分析之环境准备篇
前言 之前一段时间写了[Spring源码分析]系列的文章,感觉对Spring的原理及使用各方面都掌握了不少,趁热打铁,开始下一个系列的文章[MyBatis源码分析],在[MyBatis源码分析]文章的 ...
- Mybatis源码分析-BaseExecutor
根据前文Mybatis源码分析-SqlSessionTemplate的简单分析,对于SqlSession的CURD操作都需要经过Executor接口的update/query方法,本文将分析下Base ...
- mybatis源码分析(一)
mybatis源码分析(sqlSessionFactory生成过程) 1. mybatis框架在现在各个IT公司的使用不用多说,这几天看了mybatis的一些源码,赶紧做个笔记. 2. 看源码从一个d ...
- 【MyBatis源码分析】环境准备
前言 之前一段时间写了[Spring源码分析]系列的文章,感觉对Spring的原理及使用各方面都掌握了不少,趁热打铁,开始下一个系列的文章[MyBatis源码分析],在[MyBatis源码分析]文章的 ...
- MyBatis 源码分析-项目总览
MyBatis 源码分析-项目总览 1.概述 本文主要大致介绍一下MyBatis的项目结构.引用参考资料<MyBatis技术内幕> 此外,https://mybatis.org/mybat ...
- (一) Mybatis源码分析-解析器模块
Mybatis源码分析-解析器模块 原创-转载请说明出处 1. 解析器模块的作用 对XPath进行封装,为mybatis-config.xml配置文件以及映射文件提供支持 为处理动态 SQL 语句中的 ...
随机推荐
- 超简便安装mysql
CentOS7默认数据库是mariadb,配置等用着不习惯,因此决定改成mysql,但是CentOS7的yum源中默认好像是没有mysql的.为了解决这个问题,我们要先下载mysql的repo源. 1 ...
- 服务号使用微信网页授权(H5应用等)
获取授权准备 AppId 服务号已经认证且获取到响应接口权限 设置网页授权域名 公众号设置 - 功能设置 - 网页授权域名.注意事项: 回调页面域名或路径需使用字母.数字及"-"的 ...
- discuz伪静态设置
Discuz! 通用伪静态 -包含所有类型主机本人找了一下午才找到的,谢谢这位原创者,发出来让大家用. 第一步:打开后台 全局 SEO设置 全部打勾<ignore_js_op> ...
- [写出来才有价值系列:node.js]node.js 02-,learnyounode
安装learnyounode: npm install g learnyounode 官方说直接 但是我发现不行,很慢几乎就是死在那里了 还好有淘宝的东西给我们用https://npm.taobao. ...
- 在rhel7上搭建centos7的yum源
1. 再查看现在主机上的yum源,并将它们删除 [root@localhost ~]# rpm -qa|grep yum | xargs rpm -e --nodeps # --nodeps 不管有没 ...
- MySQL JDBC驱动下载
下载地址:https://pan.baidu.com/s/1VLNaV_rz2P1jMtYrjJydiQ
- 创建 dblink
目的:oracle中跨数据库查询 两台数据库服务器db_A(本地)和db_B(远程192.168.1.100),db_A下用户user_a 需要访问到db_B下user_b的数据解决:查询 ...
- 以太坊go-ethereum客户端(三)两种全节点启动模式
这篇博客介绍一下go-ethereum全节点的两种启动模式:主网络快速启动和测试网络快速启动.这也是客户端所提供的两种启动方式,直接拿来使用即可.下面具体介绍一下使用方法. 主网络快速启动 其实,我们 ...
- Tomcat集群的session共享
配置Tomcat的session共享可以有三种解决方案: 第一种是以负载均衡服务器本身提供的session共享策略,每种服务器的配置是不一样的并且nginx本身是没有的. 第二种是利用web容器本身的 ...
- oracle 12C wmsys.wm_concat()函数
http://blog.itpub.net/31392094/viewspace-2149577/