mybatis源码分析(3)-----SqlSessionHolder作用

摘要:
如果没有持有者或未被事务锁定,将通过sessionFactory创建一个新的sqlSession,即CreatingnewSqlSession。openSession()方法。如果当前线程的事务处于活动状态,则将为SqlSession注册事务同步,即为SqlSession(空)注册事务同步{//如果持有SqlSession的引用,则可以直接获取returnsession;}如果{LOGGER.debug;}//获取新的sqlSession对象。这里,defaultSqlSessionsession=sessionFactory。sessionFactory生成的openSession//判断是否有事务,将sqlSession绑定到sqlSessionHolder,放入threadLoacl中注册SessionHolder;returnsession;}privatestaticSqlSessionsessionHolder{SqlSessionsession=null;if(holder!

1、 sqlSessionHolder 是位于mybatis-spring 包下面,他的作用是对于sqlSession和事务的控制

mybatis源码分析(3)-----SqlSessionHolder作用第1张

  • 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作用》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇mysql中json取,查,改,去双引号lua type 获取 类型下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

主流RPC框架通讯协议实现原理与源码解析

主流RPC框架通讯协议实现原理与源码解析。互联网+的大环境下,用户量、数据量的急剧增长,使得单机系统不能承载更多的数据处理能力,从而催生了分布式技术的快速发展。 分布式RPC框架,已经有很多开源的高性能框架,例如Dubbo、GRpc、Spring Cloud,他们都是非常优秀的RPC框架。这个PPT主要是用来分析常见RPC框架的实现原理和源码解析,最后通...

ORTP库API使用入门

一、简介 ORTP是一个支持RTP以及RFC3550协议的库,有如下的特性: (1)使用C语言编写,可以工作于windows, Linux, 以及 Unix平台 (2)实现了RFC3550协议,提供简单易用的API。支持多种配置,RFC3551为默认的配置。 (3)支持单线程下的多个RTP会话,支持自适应抖动处理。 (4)基于GPL版权声明。 ORTP可以...

SpringBoot整合SpringSecurity,SESSION 并发管理,同账号只允许登录一次

重写了UsernamePasswordAuthenticationFilter,里面继承AbstractAuthenticationProcessingFilter,这个类里面的session认证策略,是一个空方法,貌似RememberMe也是. public abstract class AbstractAuthenticationProcessingF...

【MyBatis源码分析】Configuration加载(下篇)

元素设置 继续MyBatis的Configuration加载源码分析: 1 private void parseConfiguration(XNode root) { 2 try { 3 Properties settings = settingsAsPropertiess(root.evalNode("settings"))...

MySQL学习笔记——〇一

这里不讲MySQL的原理和连接的方法了,就讲一下如何对数据库进行操作。 用户操作 创建用户 创建用户的方法:我们可以用下面的代码进行用户的创建 create user 'username'@'ip' identified by 'password'; 在上面的代码中,表示创建了用户名为username的用户,用户登录ip限制为ip,密码为password。...

Mybatis中的#与$的区别

一、对比场景 场景:数据库分表时,需要将分表的表序号传入的sql中。 SpringBoot中使用注解如下: @Insert("insert into collect_#{tblNum}(id,user_id,resource_id,resource_name,author,album,resource_type,create_time,update_tim...