天天看點

關于MyBatis sqlSession的一點整理

    工作中,需要學習一下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
        >
             

其中使用到了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;
       
 
        }      
  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。