Mybatis系列之五 SqlSessionTemplate

一、思路

在之前的文章中,我們已經(jīng)講了

  1. mybatis初始化解析XML文件,將生成的MapperStatement注冊到MapperRegister中;
  2. 如何在spring中獲取MapperProxy的代理;
  3. MapperProxy代理對象背后的執(zhí)行邏輯(調(diào)用了MapperMethod來執(zhí)行命令);

ok,那我們接下來就來看看mybatis到底是怎么實現(xiàn)事務的,怎么執(zhí)行SQL的。
本篇的重點是下面幾點:

  1. SqlSessionTemplate 類簡介.
  2. SqlSessionTemplate 如何初始化.
  3. SqlSessionTemplate增刪改查背后的邏輯(怎么與spring的事務進行融合).

二、SqlSessionTemplate 類簡介

這是作者給出的對SqlSessionTemplate類的說明,SqlSessionTemplate是線程安全的,生命周期由spring管理的,同spring事務一起協(xié)作來保證真正執(zhí)行的SqlSession是在spring的事務中的一個SqlSession的實現(xiàn)類。(強行翻譯,看作者原文即可。。)

/** Thread safe, Spring managed, {@code SqlSession} that works with Spring
 * transaction management to ensure that that the actual SqlSession used is the
 * one associated with the current Spring transaction. In addition, it manages
 * the session life-cycle, including closing, committing or rolling back the
 * session as necessary based on the Spring transaction configuration.
*/

再看一下SqlSessionTemplate的繼承結(jié)構(gòu),它也是一個SqlSession接口的實現(xiàn)類。


SqlSessionTemplate.png

再觀察一下SqlSessionTemplate的屬性,SqlSessionTemplate下有一個代理類,由JDK動態(tài)代理生成,SqlSessionTemplate真正執(zhí)行增刪改查都是調(diào)用代理類的接口。

// 代理類
private final SqlSession sqlSessionProxy;
// ... 省略部分代碼
// 構(gòu)造函數(shù)
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動態(tài)代理工具類生成了一個SqlSession的代理類
    this.sqlSessionProxy = (SqlSession) newProxyInstance(
        SqlSessionFactory.class.getClassLoader(),
        new Class[] { SqlSession.class },
        new SqlSessionInterceptor());
  }
  // ...
  // 調(diào)用代理類實現(xiàn)
  @Override
  public <T> T selectOne(String statement) {
    return this.sqlSessionProxy.<T> selectOne(statement);
  }

三、SqlSessionTemplate 如何初始化.

????????想真正理解一個類,就看看它是怎么初始化的。在Mybatis,Spring系列之Mapper對象獲取一文中,我們描述了Spring如何獲取Mapper對象,其實提到了其實是在MapperFactoryBean對象調(diào)用它的私有屬性SqlSession對象的getMapper()方法來獲取已經(jīng)注冊到MapperRegister中的對象。那我們就從MapperFactoryBean這個類出發(fā),看看它的私有屬性SqlSession是怎么初始化的。最終我們在MapperFactoryBean的父類SqlSessionDaoSupport中看到了它。

public abstract class SqlSessionDaoSupport extends DaoSupport {
  public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
    if (!this.externalSqlSession) {
      this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
    }
  }
}

進到SqlSessionTemplate的構(gòu)造方法中,可以看到它內(nèi)部的代理對象的背后就是SqlSessionInterceptor子類,具體的邏輯執(zhí)行都是靠它來完成。

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;
    this.sqlSessionProxy = (SqlSession) newProxyInstance(
        SqlSessionFactory.class.getClassLoader(),
        new Class[] { SqlSession.class },
        new SqlSessionInterceptor());
  }

到這兒,SqlSessionTemplate的初始化算是講完了,接下來我們看看它是怎么和spring事務結(jié)合的。

四、SqlSessionTemplate增刪改查背后的邏輯(怎么與spring的事務進行融合)

Mybatis系列之MappMethod(揭示Mapper類背后的執(zhí)行邏輯)
文章中將到了,MapperProxy對象發(fā)起請求的最終執(zhí)行對象還是SqlSession,通過調(diào)用SqlSession的增刪改查接口來完成請求,Spring使用的又是SqlSessionTemplate對象,而SqlSessionTemplate最終又是依賴其代理類來完成任務,那我們就重點來分析SqlSessionInterceptor這個類。

private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      // 獲取真正執(zhí)行任務的SqlSession,其實就是Mybatis自身的DefaultSqlSession
      SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
      try {
        // 執(zhí)行命令
        Object result = method.invoke(sqlSession, args);
        // 如果事務不由spring進行管理,那么就提交事務
        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) {
          // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
          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);
        }
      }
    }
  }

可以看到,真正執(zhí)行命令的是通過getSqlSession()方法拿到的SqlSession實現(xiàn)類。

public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {

    notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
    notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
    // 根據(jù)sqlSessionFactory從當前線程對應的事務管理器中獲取SqlSessionHolder,當sqlSessionFactory創(chuàng)建了sqlSession,就會在事務管理器中添加一對映射:key為sqlSessionFactory,value為SqlSessionHolder,該類保存sqlSession及執(zhí)行方式 
    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);

    // 拿到同一個事務中所使用的SqlSession
    SqlSession session = sessionHolder(executorType, holder);
    if (session != null) {
      return session;
    }

    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("Creating a new SqlSession");
    }

    // 如無SqlSession,則實例化一個SqlSession,并新建一個事務
    session = sessionFactory.openSession(executorType);

    // 注冊到事務管理器中,方便同一個事務中的其他方法使用
    registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);

    return session;
  }

總結(jié)一下,SqlSessionTemplate主要是幫助Spring管理Mybatis的事務,通過代理模式屏蔽了SqlSession的實現(xiàn)細節(jié),專注于管理SqlSession的事務??梢越Y(jié)合aop的方式,幫助用戶自動去提交,回滾事務,不可謂不強大。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容