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(); } }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM