MyBatis sqlSession概要梳理

FeelTouch發表於2018-08-29

工作中,需要學習一下MyBatis sqlSession的產生過程,翻看了mybatis-spring的原始碼,閱讀了一些mybatis的相關doc,對mybatis sqlSession有了一些認知和理解,這裡簡單的總結和整理一下。

 

    首先, 通過翻閱原始碼,我們來整理一下mybatis進行持久化操作時重要的幾個類:

  • SqlSessionFactoryBuilder:build方法建立SqlSessionFactory例項。

  • SqlSessionFactory:建立SqlSession例項的工廠。

  • SqlSession:用於執行持久化操作的物件,類似於jdbc中的Connection。

  • SqlSessionTemplate:MyBatis提供的持久層訪問模板化的工具,執行緒安全,可通過構造引數或依賴注入SqlSessionFactory例項。

 

    Hibernate是與MyBatis類似的orm框架,這裡與Hibernate進行一下對比,Hibernate中對於connection的管理,是通過以下幾個重要的類:

  • SessionFactory:建立Session例項的工廠,類似於MyBatis中的SqlSessionFactory。

  • Session:用來執行持久化操作的物件,類似於jdbc中的Connection。

  • HibernateTemplate:Hibernate提供的持久層訪問模板化的工具,執行緒安全,可通過構造引數或依賴注入SessionFactory例項。

 

    在日常的開發中,我們經常需要這樣對MyBatis和Spring進行整合,把sqlSessionFactory交給Spring管理,通常情況下,我們這樣配置:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
</bean>

    通過上面的配置,Spring將自動建立一個SqlSessionFactory物件,其中使用到了org.mybatis.spring.SqlSessionFactoryBean,其 是MyBatis為Spring提供的用於建立SqlSessionFactory的類,將在Spring應用程式的上下文建議一下可共享的 MyBatis SqlSessionFactory例項,我們可以通過依賴注入將SqlSessionFactory傳遞給MyBatis的一些介面。

 

    如果通過Spring進行事務的管理,我們需要增加Spring註解的事務管理機制,如下配置:

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>
 
<tx:annotation-driven/>

    

    這樣,我們就可以使用Spring @Transactional註解,進行事務的控制,表明所註釋的方法應該在一個事務中執行。 Spring將在事務成功完成後提交事務,在事務發生錯誤時進行異常回滾,而且,Spring會將產生的MyBatis異常轉換成適當的 DataAccessExceptions,從而提供具體的異常資訊。

 

    下面,我們通過分析SqlSessionUtils中getSession的原始碼,來詳細的瞭解一下sqlSession的產生過程,原始碼如下:

public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
 
  notNull(sessionFactory, "No SqlSessionFactory specified");
  notNull(executorType, "No ExecutorType specified");
 
  SqlSessionHolder holder = (SqlSessionHolder) getResource(sessionFactory);
 
  if (holder != null && holder.isSynchronizedWithTransaction()) {
    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");
    }
 
    return holder.getSqlSession();
  }
 
  if (logger.isDebugEnabled()) {
    logger.debug("Creating a new SqlSession");
  }
 
  SqlSession session = sessionFactory.openSession(executorType);
 
  // Register session holder if synchronization is active (i.e. a Spring TX is active)
  //
  // Note: The DataSource used by the Environment should be synchronized with the
  // transaction either through DataSourceTxMgr or another tx synchronization.
  // Further assume that if an exception is thrown, whatever started the transaction will
  // handle closing / rolling back the Connection associated with the SqlSession.
  if (isSynchronizationActive()) {
    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);
      bindResource(sessionFactory, holder);
      registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
      holder.setSynchronizedWithTransaction(true);
      holder.requested();
    } else {
      if (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");
    }
  }
 
  return session;
}

    上面的getSession方法,會從Spring的事務管理器中獲取一個SqlSession或建立一個新的SqlSession,將試圖從當前事務中得到一個SqlSession,然後,如果配置有事務管理器的工廠並且Spring 的事務管理器是活躍的,它將會鎖定當前事務的SqlSession,保證同步。主要是通過以下幾個步驟進行SqlSession的建立:

  1. 它會首先獲取SqlSessionHolder,SqlSessionHolder用於在TransactionSynchronizationManager中保持當前的SqlSession。

  2. 如果holder不為空,並且holder被事務鎖定,則可以通過holder.getSqlSession()方法,從當前事務中獲取sqlSession,即 Fetched SqlSession from current transaction。

  3. 如果不存在holder或沒有被事務鎖定,則會建立新的sqlSession,即 Creating a new SqlSession,通過sessionFactory.openSession()方法。

  4. 如果當前執行緒的事務是活躍的,將會為SqlSession註冊事務同步,即 Registering transaction synchronization for SqlSession。

相關文章