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; }
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (LOGGER.isDebugEnabled()) {
  LOGGER.debug(</span>"Creating a new SqlSession"<span style="color: rgba(0, 0, 0, 1)">);
}
//获取新的sqlSession 对象。这里由sessionFacory产生的defaultSqlSession
session </span>=<span style="color: rgba(0, 0, 0, 1)"> sessionFactory.openSession(executorType);<br>    //判断判断,当前是否存在事务,将sqlSession 绑定到sqlSessionHolder 中,并放到threadLoacl 当中
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> 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();
      </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (LOGGER.isDebugEnabled()) {
        LOGGER.debug(</span>"Fetched SqlSession [" + holder.getSqlSession() + "] from current transaction"<span style="color: rgba(0, 0, 0, 1)">);
      }
      //<span class="comment">返回sqlSession&nbsp;</span>
      session </span>=<span style="color: rgba(0, 0, 0, 1)"> holder.getSqlSession();
    }
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> 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 </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> SqlSessionHolder(session, executorType, exceptionTranslator);<br>       //如果当前回话处在事务当中,则将holder 绑定到ThreadLocal 中<br>        <span class="comment">//以sessionFactory为key,hodler为value,加入到TransactionSynchronizationManager管理的本地缓存ThreadLocal&lt;Map&lt;Object,&nbsp;Object&gt;&gt;&nbsp;resources中&nbsp;</span>
    TransactionSynchronizationManager.bindResource(sessionFactory, holder);<br>        <span class="comment">//将holder,&nbsp;sessionFactory的同步加入本地线程缓存中ThreadLocal&lt;Set&lt;TransactionSynchronization&gt;&gt;&nbsp;synchronizations&nbsp;</span>
    TransactionSynchronizationManager.registerSynchronization(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> SqlSessionSynchronization(holder, sessionFactory));<br>        /<span class="comment">/设置当前holder和当前事务同步&nbsp;</span>
    holder.setSynchronizedWithTransaction(</span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">);<br>        //holder 引用次数+1
    holder.requested();
  } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (TransactionSynchronizationManager.getResource(environment.getDataSource()) == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
      </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (LOGGER.isDebugEnabled()) {
        LOGGER.debug(</span>"SqlSession [" + session + "] was not registered for synchronization because DataSource is not transactional"<span style="color: rgba(0, 0, 0, 1)">);
      }
    } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
      </span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> TransientDataAccessResourceException(
          </span>"SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization"<span style="color: rgba(0, 0, 0, 1)">);
    }
  }
} </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
  </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (LOGGER.isDebugEnabled()) {
    LOGGER.debug(</span>"SqlSession [" + session + "] was not registered for synchronization because synchronization is not active"<span style="color: rgba(0, 0, 0, 1)">);
  }
}

}

 

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 </span>=<span style="color: rgba(0, 0, 0, 1)"> (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);<br>    //如果holder 中持有sqlSession 的引用,(即会话存在事务)
</span><span style="color: rgba(0, 0, 255, 1)">if</span> ((holder != <span style="color: rgba(0, 0, 255, 1)">null</span>) &amp;&amp; (holder.getSqlSession() ==<span style="color: rgba(0, 0, 0, 1)"> session)) {
  </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (LOGGER.isDebugEnabled()) {
    LOGGER.debug(</span>"Releasing transactional SqlSession [" + session + "]"<span style="color: rgba(0, 0, 0, 1)">);
  }<br>    //每当一个sqlSession 执行完毕,则减少holder 持有引用的次数
  holder.released();
} </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
  </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (LOGGER.isDebugEnabled()) {
    LOGGER.debug(</span>"Closing non transactional SqlSession [" + session + "]"<span style="color: rgba(0, 0, 0, 1)">);
  }<br>      //如果回话中,不存在事务,则直接关闭session
  session.close();
}

}