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作用的更多相关文章

  1. 精尽MyBatis源码分析 - MyBatis-Spring 源码分析

    该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...

  2. MyBatis源码分析-MyBatis初始化流程

    MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简 ...

  3. MyBatis源码分析(3)—— Cache接口以及实现

    @(MyBatis)[Cache] MyBatis源码分析--Cache接口以及实现 Cache接口 MyBatis中的Cache以SPI实现,给需要集成其它Cache或者自定义Cache提供了接口. ...

  4. 【MyBatis源码分析】select源码分析及小结

    示例代码 之前的文章说过,对于MyBatis来说insert.update.delete是一组的,因为对于MyBatis来说它们都是update:select是一组的,因为对于MyBatis来说它就是 ...

  5. MyBatis源码分析之环境准备篇

    前言 之前一段时间写了[Spring源码分析]系列的文章,感觉对Spring的原理及使用各方面都掌握了不少,趁热打铁,开始下一个系列的文章[MyBatis源码分析],在[MyBatis源码分析]文章的 ...

  6. Mybatis源码分析-BaseExecutor

    根据前文Mybatis源码分析-SqlSessionTemplate的简单分析,对于SqlSession的CURD操作都需要经过Executor接口的update/query方法,本文将分析下Base ...

  7. mybatis源码分析(一)

    mybatis源码分析(sqlSessionFactory生成过程) 1. mybatis框架在现在各个IT公司的使用不用多说,这几天看了mybatis的一些源码,赶紧做个笔记. 2. 看源码从一个d ...

  8. 【MyBatis源码分析】环境准备

    前言 之前一段时间写了[Spring源码分析]系列的文章,感觉对Spring的原理及使用各方面都掌握了不少,趁热打铁,开始下一个系列的文章[MyBatis源码分析],在[MyBatis源码分析]文章的 ...

  9. MyBatis 源码分析-项目总览

    MyBatis 源码分析-项目总览 1.概述 本文主要大致介绍一下MyBatis的项目结构.引用参考资料<MyBatis技术内幕> 此外,https://mybatis.org/mybat ...

  10. (一) Mybatis源码分析-解析器模块

    Mybatis源码分析-解析器模块 原创-转载请说明出处 1. 解析器模块的作用 对XPath进行封装,为mybatis-config.xml配置文件以及映射文件提供支持 为处理动态 SQL 语句中的 ...

随机推荐

  1. Google Gapps – Download Gapps for Android【转】

    http://wiki.rootzwiki.com/Google_Apps http://productforums.google.com/forum/#!forum/apps http://www. ...

  2. 64_s2

    sipwitch-1.9.15-3.fc26.x86_64.rpm 13-Feb-2017 09:19 162822 sipwitch-cgi-1.9.15-3.fc26.x86_64.rpm 13- ...

  3. 136.Single Number---异或、位运算

    题目链接 题目大意:给出一串数组,里面的数都是两个,只有一个数是一个,把这个只有一个的数找出来.时间复杂度最好是线性的,空间复杂度最好为O(1). 法一:利用map,空间换时间,代码如下(耗时26ms ...

  4. css预处理scss环境配置

    css 预处理器 CSS 预处理器用一种专门的编程语言,进行 Web css编码,然后再编译成正常的 CSS 文件,以供项目使用:说简单点就是在某个环境下写css 可以写变量.表达式.嵌套等,在通过该 ...

  5. vue头像上传

    项目四知识点 默认头像 选择头像 <template> <div class="adatar"> <img :src="adatar?ada ...

  6. java实现ftp文件上传下载,解决慢,中文乱码,多个文件下载等问题

    //文件上传 public static boolean uploadToFTP(String url,int port,String username,String password,String ...

  7. ansible安装配置及最佳实践roles

    ansible是什么? ansible是一款轻量级配置管理工具,用于远程批量部署.安装.配置.类似的还有puppet.saltstack,各有所长,任君自选. 官方文档:http://docs.ans ...

  8. Bootstrap的使用。。。

    概览 深入了解 Bootstrap 底层结构的关键部分,包括我们让 web 开发变得更好.更快.更强壮的最佳实践. HTML5 文档类型 Bootstrap 使用到的某些 HTML 元素和 CSS 属 ...

  9. python和C语言的差别

    之前在公司一直做的是C语言的开发,然后做的都是业务方面的东西,做的是sdk,因为最近在找工作,然后今天面试的时候被问到C语言和Python的区别,自己只是简单的说了C是静态语言,在变量在使用前进行声明 ...

  10. appium--【Mac】提示报错“could not launch WebDriverAgentRunner..........."

    运行appium   WebDriverAgentLib和WebDriverAgentRunner都编译到真机运行成功,未在桌面生成一个没图标的WebDriverAgentRunner 连接并选择自己 ...