Mybatis与Spring集成:SqlSessionTemplate[通俗易懂]

Mybatis与Spring集成:SqlSessionTemplate[通俗易懂]Mybatis与Spring集成:SqlSessionTemplateSqlSessionTemplate构造方法publicSqlSessionTemplate(SqlSessionFactorysqlSessionFactory,ExecutorTypeexecutorType,PersistenceExceptionTranslatorexceptionTra…

大家好,又见面了,我是你们的朋友全栈君。

Mybatis与Spring集成:SqlSessionTemplate

SqlSessionTemplate构造方法

  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator) { 
   

    notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
    notNull(executorType, "Property 'executorType' is required");

    this.sqlSessionFactory = sqlSessionFactory;
    this.executorType = executorType;
    this.exceptionTranslator = exceptionTranslator;
    //使用JDK动态代理,创建SqlSessionFactory的代理类的实例 
    this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
        new Class[] { 
    SqlSession.class },new SqlSessionInterceptor());
  }

核心在SqlSessionInterceptor的invoke方法中

  private class SqlSessionInterceptor implements InvocationHandler { 
   
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
   
        //重要:获取sqlSession,具体操作见下方详解
      SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
      try { 
   
        //调用真实SqlSession的操作方法
        Object result = method.invoke(sqlSession, args);
        //判断当前sqlSession是否被Spring托管;未被Spring托管则自动commit
        if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) { 
   
          // force commit even on non-dirty sessions because some databases require a commit/rollback before calling close()
          sqlSession.commit(true);
        }
        return result;
      } catch (Throwable t) { 
   
        Throwable unwrapped = unwrapThrowable(t);
        if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) { 
   
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
          sqlSession = null;
          Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
          if (translated != null) { 
   
            unwrapped = translated;
          }
        }
        throw unwrapped;
      } finally { 
   
        if (sqlSession != null) { 
   
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
      }
    }
  }

SqlSessionUtils.getSqlSession方法

  //从事务管理器中获取sqlSession或创建一个 
  public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { 
   

    notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
    notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
    //根据sqlSessionFactory从当前线程对应的资源map中获取SqlSessionHolder,当sqlSessionFactory创建了sqlSession,就会在事务管理器中添加一对映射:key为sqlSessionFactory,value为SqlSessionHolder,该类保存sqlSession及执行方式 
    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
    //返回holder中的sqlSession,见下方
    SqlSession session = sessionHolder(executorType, holder);
    if (session != null) { 
   
      return session;
    }

    if (LOGGER.isDebugEnabled()) { 
   
      LOGGER.debug("Creating a new SqlSession");
    }
    // 如果holder中返回的sqlSession为空,则创建sqlSession,并注册到holder中去
    session = sessionFactory.openSession(executorType);
    registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);

    return session;
  }
  private static SqlSession sessionHolder(ExecutorType executorType, SqlSessionHolder holder) { 
   
    SqlSession session = null;
    if (holder != null && holder.isSynchronizedWithTransaction()) { 
   
      // 在同一个事务中,执行类型不能变化,原因就是同一个事务中同一个sqlSessionFactory创建的sqlSession会被重用 
      if (holder.getExecutorType() != executorType) { 
   
        throw new TransientDataAccessResourceException("Cannot change the ExecutorType when there is an existing transaction");
      }

      holder.requested();

      if (LOGGER.isDebugEnabled()) { 
   
        LOGGER.debug("Fetched SqlSession [" + holder.getSqlSession() + "] from current transaction");
      }

      session = holder.getSqlSession();
    }
    return session;
  }

如何将sqlSession注册到holder中去?

  //开启了事务管理时(如springboot常用的的@Transactional),注册session
  //Register session holder if synchronization is active (i.e. a Spring TX is active).
  private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator, SqlSession session) { 
   
    SqlSessionHolder holder;
    ///判断同步是否激活,只要SpringTX被激活,就是true 
    if (TransactionSynchronizationManager.isSynchronizationActive()) { 
   
      //加载环境变量,判断注册的事务管理器是否是Spring管理事务,如果是,则将sqlSession加载进事务管理的本地线程缓存中;否则报错
      Environment environment = sessionFactory.getConfiguration().getEnvironment();
      if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) { 
   
        if (LOGGER.isDebugEnabled()) { 
   
          LOGGER.debug("Registering transaction synchronization for SqlSession [" + session + "]");
        }
        holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
        // 以sessionFactory为key,hodler为value,加入到TransactionSynchronizationManager管理的本地缓存ThreadLocal<Map<Object, Object>>中 
        TransactionSynchronizationManager.bindResource(sessionFactory, holder);
        TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
        //设置当前holder和当前事务同步
        holder.setSynchronizedWithTransaction(true);
        //增加引用数 
        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");
      }
    }
}

这里用到了TransactionSynchronizationManager类:

TransactionSynchronizationManager.bindResource:在调用一个需要事务的组件的时候,管理器首先判断当前线程有没有事务,如果没有事务则启动一个事务,并把事务与当前线程绑定。Spring使用TransactionSynchronizationManager的bindResource方法将当前线程与一个事务绑定,采用的方式是ThreadLocal。

    public static void bindResource(Object key, Object value) throws IllegalStateException { 
   
        Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
        Assert.notNull(value, "Value must not be null");
        // 此处resources是一个全局变量: 
        //private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal("Transactional resources");
        Map<Object, Object> map = (Map)resources.get();
        if (map == null) { 
   
            map = new HashMap();
            resources.set(map);
        }
        //判断以此sessionFactory为key的Map是否存在。如果存在则此线程已经绑定了 
        Object oldValue = ((Map)map).put(actualKey, value);
        if (oldValue instanceof ResourceHolder && ((ResourceHolder)oldValue).isVoid()) { 
   
            oldValue = null;
        }

        if (oldValue != null) { 
   
            throw new IllegalStateException("Already value [" + oldValue + "] for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");
        } else { 
   
            if (logger.isTraceEnabled()) { 
   
                logger.trace("Bound value [" + value + "] for key [" + actualKey + "] to thread [" + Thread.currentThread().getName() + "]");
            }

        }
    }

可以看出:多线程获取SqlSession时,先从当前线程中获取SqlSessionHolder中的sqlSession;如果为空,再新建sqlSession。所以,多线程公用的是一个SqlSessionTemplate(默认bean的单例注入),但不共用一个SqlSession。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/138199.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 并发队列ConcurrentLinkedQueue和阻塞队列LinkedBlockingQueue用法

    并发队列ConcurrentLinkedQueue和阻塞队列LinkedBlockingQueue用法

    2022年3月7日
    29
  • c语言也能写植物大战僵尸吗_植物大战僵尸僵尸写的纸条

    c语言也能写植物大战僵尸吗_植物大战僵尸僵尸写的纸条不少同学都玩过《植物大战僵尸》,最近PopCap公司又带来了新版的消息,这次高兴的轮到Xbox的用户了,日前PopCap公司公布了《植物大战僵尸》XBLA版的截图,这个版本的《植物大战僵尸》引入了多人合作与对抗模式,看图就知道好玩多了又刺激多了。 详见游戏说明,游戏视频于是,我在非常强烈的好奇心和求知欲下,自己动手写了一个简易的双人

    2022年10月23日
    0
  • 如何从从官网下载各个版本的jquery「建议收藏」

    如何从从官网下载各个版本的jquery「建议收藏」许多前端的小伙伴们可能跟我一样有选择强迫症,对于一些工具、软件等都偏爱从官网下载,尽管许多非官方网站上已经有现成的,但还是从心理上感觉官网的更正规。如今的jquery版本已经是相当多了。在jquery官网的首页上只提供了为数不多的较为流行的版本供我们下载。但是出于各种情况的考虑,我们想要自己需要的某一版本该如何从官网获取呢?步骤也是相当简单,jquery官网虽然为了页面的简洁性并未在Downl…

    2022年5月25日
    34
  • origin绘图基础1

    origin绘图基础11.绘制带有置信区间的拟合曲线分析-拟合-拟合曲线图-勾选之信贷(默认95%);图片来源:https://www.originlab.com/index.aspx?go=Products/Origin/DataAnalysis/CurveFitting置信区间估计(confidenceintervalestimate):利用估计的回归方程,对于自变量x的一个给定值x0,求出因变量y的平均值的估计区间。预测区间估计(predictionintervalestimate):利用估计

    2022年5月6日
    46
  • 看看别人是如何进行大数据测试的?

    看看别人是如何进行大数据测试的?前言:我之前是做大数据测试的,熟悉我的小伙伴应该都知道,前面我写过两篇文章《什么是大数据测试?》、《怎么进行大数据测试?我们需要具备怎样的测试能力?》,当然,这篇文章我对大数据测试介绍的比较笼统,所以今天我在详细补充一下,主要是看看别人是如何进行大数据测试的,另外我推荐在做大数据测试的同学或者将要做大数据测试的同学去看看我正在看的两本书,我想看了之后你应该是有收获的——《机器人学习测试入门与实践》、《大数据测试技术与实践》,第一本书是我20年买的,第二本书是我21年买的,总体我收获还是挺多的!看看别人是如

    2022年5月8日
    128
  • Git 换行符检查 CRLF 与 LF

    Git 换行符检查 CRLF 与 LF

    2021年11月7日
    35

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号