mybatis sqlsession與sqlsquery、transaction、connection

  • 2020 年 7 月 23 日
  • 筆記

sqlsession和connection

  • 一個sqlsession一般對應一個connection,並且mybatis默認每次獲取session都會開啟一個事務,且不自動提交事務。如果更新操作完成後不手動commit,則在連接斷開時會將更新操作回滾,一個sqlSession(一個transaction)中可以多次commit,commit後cache和statement刷新(一般一個事務(transaction)只對應一個sqlseesion,也即一個connection,分散式一個事務可以對應多個connection),只有close後同時關閉sqlsession和transaction(transaction可以由factory關閉)並返回connection。mybatis的transaction基本沒添加什麼功能,大部分僅狀態判斷後交由connection(再由jdbc)執行。可能是為了給spring等框架容易組轉自己的事務管理機制。

sqlsession與transaction

(第一個JdbcTransactionFactory、第二個defaultsqlsessonfactory):

用transactionFactory創建相應的transaction,用創建出的tx來創建executor,再用executor和配置資訊來創建defaulSqlsession

public class JdbcTransactionFactory implements TransactionFactory {
    public JdbcTransactionFactory() {
    }

    public void setProperties(Properties props) {
    }

    public Transaction newTransaction(Connection conn) {
        return new JdbcTransaction(conn);
    }

    public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
        return new JdbcTransaction(ds, level, autoCommit);
    }
}

public class DefaultSqlSessionFactory implements SqlSessionFactory {

public SqlSession openSession(ExecutorType execType, Connection connection) {
    return this.openSessionFromConnection(execType, connection);
}
 private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {//分為兩種創建(dataSource、fromConnection)
        DefaultSqlSession var8;
        try {
            boolean autoCommit;
            try {
                autoCommit = connection.getAutoCommit();
            } catch (SQLException var13) {
                autoCommit = true;
            }

            Environment environment = this.configuration.getEnvironment();
            TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
            Transaction tx = transactionFactory.newTransaction(connection);
            Executor executor = this.configuration.newExecutor(tx, execType);
            var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
        } catch (Exception var14) {
            throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var14, var14);
        } finally {
            ErrorContext.instance().reset();
        }

        return var8;
    }
 private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
        DefaultSqlSession var8;
        try {
            boolean autoCommit;
            try {
                autoCommit = connection.getAutoCommit();
            } catch (SQLException var13) {
                autoCommit = true;
            }

            Environment environment = this.configuration.getEnvironment();
            TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
            Transaction tx = transactionFactory.newTransaction(connection);
            Executor executor = this.configuration.newExecutor(tx, execType);
            var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
        } catch (Exception var14) {
            throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var14, var14);
        } finally {
            ErrorContext.instance().reset();
        }

        return var8;
    }
    

sqlsession對executor的調用

(defaultSqlsession)

public class DefaultSqlSession implements SqlSession {

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    List var5;
    try {
        MappedStatement ms = this.configuration.getMappedStatement(statement);
        var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception var9) {
        throw ExceptionFactory.wrapException("Error querying database.  Cause: " + var9, var9);
    } finally {
        ErrorContext.instance().reset();
    }

    return var5;
}
public int update(String statement, Object parameter) {
        int var4;
        try {
            this.dirty = true;
            MappedStatement ms = this.configuration.getMappedStatement(statement);
            var4 = this.executor.update(ms, this.wrapCollection(parameter));
        } catch (Exception var8) {
            throw ExceptionFactory.wrapException("Error updating database.  Cause: " + var8, var8);
        } finally {
            ErrorContext.instance().reset();
        }

        return var4;
    }
 public void commit(boolean force) {
        try {
            this.executor.commit(this.isCommitOrRollbackRequired(force));
            this.dirty = false;
        } catch (Exception var6) {
            throw ExceptionFactory.wrapException("Error committing transaction.  Cause: " + var6, var6);
        } finally {
            ErrorContext.instance().reset();
        }

    }
    
     public void rollback(boolean force) {
        try {
            this.executor.rollback(this.isCommitOrRollbackRequired(force));
            this.dirty = false;
        } catch (Exception var6) {
            throw ExceptionFactory.wrapException("Error rolling back transaction.  Cause: " + var6, var6);
        } finally {
            ErrorContext.instance().reset();
        }

    }
  • sqlsession無論執行sql語句還是對事物的管理,都會轉由executor執行

executor對sql的執行

  • (simpleExecutor extends baseExecutor) sqlsession->executor->connection
  • 由configuration以及參數生成語句處理對象 handler,再調用preaparement方法對handler進行connection的獲取與連接,之後將操作都交給了handler,經過實現了statementhandler介面的RoutingStatementHandler->PreparedStatementHandler(extends BaseStatementHandler)之後的preparedstatement等statement後,便是jdbc的excute操作與result的封裝
  • 一級快取的發生也在處理後發生
public class SimpleExecutor extends BaseExecutor {
    public SimpleExecutor(Configuration configuration, Transaction transaction) {
        super(configuration, transaction);
    }

    public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
        Statement stmt = null;

        int var6;
        try {
            Configuration configuration = ms.getConfiguration();
            StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, (ResultHandler)null, (BoundSql)null);
            stmt = this.prepareStatement(handler, ms.getStatementLog());
            var6 = handler.update(stmt);
        } finally {
            this.closeStatement(stmt);
        }

        return var6;
    }

    public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Statement stmt = null;

        List var9;
        try {
            Configuration configuration = ms.getConfiguration();
            StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
            stmt = this.prepareStatement(handler, ms.getStatementLog());
            var9 = handler.query(stmt, resultHandler);
        } finally {
            this.closeStatement(stmt);
        }

        return var9;
    }
    
     private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        Connection connection = this.getConnection(statementLog);
        Statement stmt = handler.prepare(connection, this.transaction.getTimeout());
        handler.parameterize(stmt);
        return stmt;
    }

executor對事務的處理

  • 由simpleExecutor對象處理,但用的方法都繼承自baseExecutor
  • sqlsession(帶著在sqlsessionfactory創建的transaction)->executor(transaction)->transaction.commit/close·····
  • executor 將事務管理交給了 transaction,commit/rollback等對cache和statement的清空(clearLocalCache,flushStatements)也在這裡開始進行。
  • 在sqlsession的close前,對sql語句的執行都會用getConnection創建的連接進行sql語句的執行,commit也並不會說新建一個事務(transaction),而是清空cache和statement並提交jdbc執行後的結果到mysql。此時仍可以用commit後的sqlsession和transaction進行getMapper代理並調用介面方法,只有close後兩者才會消失
public class SimpleExecutor extends BaseExecutor {

public Transaction getTransaction() {
    if (this.closed) {
        throw new ExecutorException("Executor was closed.");
    } else {
        return this.transaction;
    }
}

public void close(boolean forceRollback) {
    try {
        try {
            this.rollback(forceRollback);
        } finally {
            if (this.transaction != null) {
                this.transaction.close();
            }

        }
    } catch (SQLException var11) {
        log.warn("Unexpected exception on closing transaction.  Cause: " + var11);
    } finally {
        this.transaction = null;
        this.deferredLoads = null;
        this.localCache = null;
        this.localOutputParameterCache = null;
        this.closed = true;
    }

}
public void commit(boolean required) throws SQLException {
        if (this.closed) {
            throw new ExecutorException("Cannot commit, transaction is already closed");
        } else {
            this.clearLocalCache();
            this.flushStatements();
            if (required) {
                this.transaction.commit();
            }

        }
    }
 public void rollback(boolean required) throws SQLException {
        if (!this.closed) {
            try {
                this.clearLocalCache();
                this.flushStatements(true);
            } finally {
                if (required) {
                    this.transaction.rollback();
                }

            }
        }

    }

public void clearLocalCache() {
        if (!this.closed) {
            this.localCache.clear();
            this.localOutputParameterCache.clear();
        }

    }

transaction到connection

  • JdbcTransaction
  • 由transaction對與connection 的commit/colse進行管理。
public class JdbcTransaction implements Transaction {


public JdbcTransaction(Connection connection) {
    this.connection = connection;
}

public Connection getConnection() throws SQLException {
    if (this.connection == null) {
        this.openConnection();
    }

    return this.connection;
}

public void commit() throws SQLException {
    if (this.connection != null && !this.connection.getAutoCommit()) {
        if (log.isDebugEnabled()) {
            log.debug("Committing JDBC Connection [" + this.connection + "]");
        }

        this.connection.commit();
    }

}

public void rollback() throws SQLException {
    if (this.connection != null && !this.connection.getAutoCommit()) {
        if (log.isDebugEnabled()) {
            log.debug("Rolling back JDBC Connection [" + this.connection + "]");
        }

        this.connection.rollback();
    }

}

public void close() throws SQLException {
    if (this.connection != null) {
        this.resetAutoCommit();
        if (log.isDebugEnabled()) {
            log.debug("Closing JDBC Connection [" + this.connection + "]");
        }

        this.connection.close();
    }

}
}

connection

  • connectionImp類
  • 從這開始便是com.mysql.jdbc包的東西了,就是與資料庫直接進行交接的部分了
public class ConnectionImpl extends ConnectionPropertiesImpl implements Connection {

public void commit() throws SQLException {
    synchronized(this.getMutex()) {
        this.checkClosed();

        try {
            if (this.connectionLifecycleInterceptors != null) {
                IterateBlock iter = new IterateBlock(this.connectionLifecycleInterceptors.iterator()) {
                    void forEach(Object each) throws SQLException {
                        if (!((ConnectionLifecycleInterceptor)each).commit()) {
                            this.stopIterating = true;
                        }

                    }
                };
                iter.doForAll();
                if (!iter.fullIteration()) {
                    return;
                }
            }

            if (this.autoCommit && !this.getRelaxAutoCommit()) {
                throw SQLError.createSQLException("Can't call commit when autocommit=true");
            }

            if (this.transactionsSupported) {
                if (this.getUseLocalSessionState() && this.versionMeetsMinimum(5, 0, 0) && !this.io.inTransactionOnServer()) {
                    return;
                }

                this.execSQL((StatementImpl)null, "commit", -1, (Buffer)null, 1003, 1007, false, this.database, (Field[])null, false);
            }
        } catch (SQLException var8) {
            if ("08S01".equals(var8.getSQLState())) {
                throw SQLError.createSQLException("Communications link failure during commit(). Transaction resolution unknown.", "08007");
            }

            throw var8;
        } finally {
            this.needsPing = this.getReconnectAtTxEnd();
        }

    }
}
}

在這裡插入圖片描述

spring與MyBatis 事務管理

//coderbee.net/index.php/framework/20191025/2002

1. 運行環境 Enviroment

當 MyBatis 與不同的應用結合時,需要不同的事務管理機制。與 Spring 結合時,由 Spring 來管理事務;單獨使用時需要自行管理事務,在容器里運行時可能由容器進行管理。

MyBatis 用 Enviroment 來表示運行環境,其封裝了三個屬性:

public class Configuration {
    // 一個 MyBatis 的配置只對應一個環境
    protected Environment environment;
    // 其他屬性 .....
}

public final class Environment {
    private final String id;
    private final TransactionFactory transactionFactory;
    private final DataSource dataSource;
}

2. 事務抽象

MyBatis 把事務管理抽象出 Transaction 介面,由 TransactionFactory 介面的實現類負責創建。

public interface Transaction {
    Connection getConnection() throws SQLException;
    void commit() throws SQLException;
    void rollback() throws SQLException;
    void close() throws SQLException;
    Integer getTimeout() throws SQLException;
}

public interface TransactionFactory {
    void setProperties(Properties props);
    Transaction newTransaction(Connection conn);
    Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
}

Executor 的實現持有一個 SqlSession 實現,事務控制是委託給 SqlSession 的方法來實現的。

public abstract class BaseExecutor implements Executor {
    protected Transaction transaction;

    public void commit(boolean required) throws SQLException {
        if (closed) {
            throw new ExecutorException("Cannot commit, transaction is already closed");
        }
        clearLocalCache();
        flushStatements();
        if (required) {
            transaction.commit();
        }
    }

    public void rollback(boolean required) throws SQLException {
        if (!closed) {
            try {
                clearLocalCache();
                flushStatements(true);
            } finally {
                if (required) {
                    transaction.rollback();
                }
            }
        }
    }

    // 省略其他方法、屬性
}

3. 與 Spring 集成的事務管理

3.1 配置 TransactionFactory

與 Spring 集成時,通過 SqlSessionFactoryBean 來初始化 MyBatis 。

protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
    Configuration configuration;

    XMLConfigBuilder xmlConfigBuilder = null;
    if (this.configuration != null) {
        configuration = this.configuration;
        if (configuration.getVariables() == null) {
            configuration.setVariables(this.configurationProperties);
        } else if (this.configurationProperties != null) {
            configuration.getVariables().putAll(this.configurationProperties);
        }
    } else if (this.configLocation != null) {
        xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
        configuration = xmlConfigBuilder.getConfiguration();
    } else {
        configuration = new Configuration();
        configuration.setVariables(this.configurationProperties);
    }

    if (this.objectFactory != null) {
        configuration.setObjectFactory(this.objectFactory);
    }

    if (this.objectWrapperFactory != null) {
        configuration.setObjectWrapperFactory(this.objectWrapperFactory);
    }

    if (this.vfs != null) {
        configuration.setVfsImpl(this.vfs);
    }

    if (hasLength(this.typeAliasesPackage)) {
        String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
        ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
        for (String packageToScan : typeAliasPackageArray) {
            configuration.getTypeAliasRegistry().registerAliases(packageToScan,
            typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
        }
    }

    if (!isEmpty(this.typeAliases)) {
        for (Class<?> typeAlias : this.typeAliases) {
            configuration.getTypeAliasRegistry().registerAlias(typeAlias);
        }
    }

    if (!isEmpty(this.plugins)) {
        for (Interceptor plugin : this.plugins) {
            configuration.addInterceptor(plugin);
        }
    }

    if (hasLength(this.typeHandlersPackage)) {
        String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
        ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
        for (String packageToScan : typeHandlersPackageArray) {
            configuration.getTypeHandlerRegistry().register(packageToScan);
        }
    }

    if (!isEmpty(this.typeHandlers)) {
        for (TypeHandler<?> typeHandler : this.typeHandlers) {
            configuration.getTypeHandlerRegistry().register(typeHandler);
        }
    }

    if (this.databaseIdProvider != null) {//fix #64 set databaseId before parse mapper xmls
        try {
            configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
        } catch (SQLException e) {
            throw new NestedIOException("Failed getting a databaseId", e);
        }
    }

    if (this.cache != null) {
        configuration.addCache(this.cache);
    }

    if (xmlConfigBuilder != null) {
        try {
            xmlConfigBuilder.parse();
        } catch (Exception ex) {
            throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
        } finally {
            ErrorContext.instance().reset();
        }
    }

    // 創建 SpringManagedTransactionFactory
    if (this.transactionFactory == null) {
        this.transactionFactory = new SpringManagedTransactionFactory();
    }

    // 封裝成 Environment
    configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));

    if (!isEmpty(this.mapperLocations)) {
        for (Resource mapperLocation : this.mapperLocations) {
            if (mapperLocation == null) {
                continue;
            }

            try {
                XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
                configuration, mapperLocation.toString(), configuration.getSqlFragments());
                xmlMapperBuilder.parse();
            } catch (Exception e) {
                throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
            } finally {
                ErrorContext.instance().reset();
            }
        }
    } else {
    }

    return this.sqlSessionFactoryBuilder.build(configuration);
}
public class SqlSessionFactoryBuilder {
    public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
    }
}

重點是在構建 MyBatis Configuration 對象時,把 transactionFactory 配置成 SpringManagedTransactionFactory,再封裝成 Environment 對象。

3.2 運行時事務管理

Mapper 的代理對象持有的是 SqlSessionTemplate,其實現了 SqlSession 介面。

SqlSessionTemplate 的方法並不直接調用具體的 SqlSession 的方法,而是委託給一個動態代理,通過代理 SqlSessionInterceptor 對方法調用進行攔截。

SqlSessionInterceptor 負責獲取真實的與資料庫關聯的 SqlSession 實現,並在方法執行完後決定提交或回滾事務、關閉會話。

public class SqlSessionTemplate implements SqlSession, DisposableBean {
    private final SqlSessionFactory sqlSessionFactory;
    private final ExecutorType executorType;
    private final SqlSession sqlSessionProxy;
    private final PersistenceExceptionTranslator exceptionTranslator;

    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;

        // 因為 SqlSession 介面聲明的方法也不少,
        // 在每個方法里添加事務相關的攔截比較麻煩,
        // 不如創建一個內部的代理對象進行統一處理。
        this.sqlSessionProxy = (SqlSession) newProxyInstance(
            SqlSessionFactory.class.getClassLoader(),
            new Class[] { SqlSession.class },
            new SqlSessionInterceptor());
    }

    public int update(String statement) {
        // 在代理對象上執行方法調用
        return this.sqlSessionProxy.update(statement);
    }

    // 對方法調用進行攔截,加入事務控制邏輯
    private class SqlSessionInterceptor implements InvocationHandler {
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 獲取與資料庫關聯的會話
            SqlSession sqlSession = SqlSessionUtils.getSqlSession(
                SqlSessionTemplate.this.sqlSessionFactory,
                SqlSessionTemplate.this.executorType,
                SqlSessionTemplate.this.exceptionTranslator);
            try {
                // 執行 SQL 操作
                Object result = method.invoke(sqlSession, args);
                if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
                    // 如果 sqlSession 不是 Spring 管理的,則要自行提交事務
                    sqlSession.commit(true);
                }
                return result;
            } catch (Throwable t) {
                Throwable unwrapped = unwrapThrowable(t);
                if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {

                    SqlSessionUtils.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) {
                    SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
                }
            }
        }
    }
}

SqlSessionUtils 封裝了對 Spring 事務管理機制的訪問。

// SqlSessionUtils
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
    // 從 Spring 的事務管理機制那裡獲取當前事務關聯的會話
    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);

    SqlSession session = sessionHolder(executorType, holder);
    if (session != null) {
        // 已經有一個會話則復用
        return session;
    }

    // 創建新的 會話
    session = sessionFactory.openSession(executorType);

    // 註冊到 Spring 的事務管理機制里
    registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);

    return session;
}

private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType,
        PersistenceExceptionTranslator exceptionTranslator, SqlSession session) {
    SqlSessionHolder holder;
    if (TransactionSynchronizationManager.isSynchronizationActive()) {
        Environment environment = sessionFactory.getConfiguration().getEnvironment();

        if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
            holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
            TransactionSynchronizationManager.bindResource(sessionFactory, holder);

            // 重點:註冊會話管理的回調鉤子,真正的關閉動作是在回調里完成的。
            TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
            holder.setSynchronizedWithTransaction(true);

            // 維護會話的引用計數
            holder.requested();
        } else {
            if (TransactionSynchronizationManager.getResource(environment.getDataSource()) == null) {
            } else {
                throw new TransientDataAccessResourceException(
                    "SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization");
            }
        }
    } else {
    }
}

public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
    // 從執行緒本地變數里獲取 Spring 管理的會話
    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
    if ((holder != null) && (holder.getSqlSession() == session)) {
        // Spring 管理的不直接關閉,由回調鉤子來關閉
        holder.released();
    } else {
        // 非 Spring 管理的直接關閉
        session.close();
    }
}

SqlSessionSynchronizationSqlSessionUtils 的內部私有類,用於作為回調鉤子與 Spring 的事務管理機制協調工作,TransactionSynchronizationManager 在適當的時候回調其方法。

private static final class SqlSessionSynchronization extends TransactionSynchronizationAdapter {
    private final SqlSessionHolder holder;
    private final SqlSessionFactory sessionFactory;
    private boolean holderActive = true;

    public SqlSessionSynchronization(SqlSessionHolder holder, SqlSessionFactory sessionFactory) {
        this.holder = holder;
        this.sessionFactory = sessionFactory;
    }

    public int getOrder() {
        return DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 1;
    }

    public void suspend() {
        if (this.holderActive) {
            TransactionSynchronizationManager.unbindResource(this.sessionFactory);
        }
    }

    public void resume() {
        if (this.holderActive) {
            TransactionSynchronizationManager.bindResource(this.sessionFactory, this.holder);
        }
    }

    public void beforeCommit(boolean readOnly) {
        if (TransactionSynchronizationManager.isActualTransactionActive()) {
            try {
                this.holder.getSqlSession().commit();
            } catch (PersistenceException p) {
                if (this.holder.getPersistenceExceptionTranslator() != null) {
                    DataAccessException translated = this.holder
                        .getPersistenceExceptionTranslator()
                        .translateExceptionIfPossible(p);
                    if (translated != null) {
                        throw translated;
                    }
                }
                throw p;
            }
        }
    }

    public void beforeCompletion() {
        if (!this.holder.isOpen()) {
            TransactionSynchronizationManager.unbindResource(sessionFactory);
            this.holderActive = false;

            // 真正關閉資料庫會話
            this.holder.getSqlSession().close();
        }
    }

    public void afterCompletion(int status) {
        if (this.holderActive) {
            TransactionSynchronizationManager.unbindResourceIfPossible(sessionFactory);
            this.holderActive = false;

            // 真正關閉資料庫會話
            this.holder.getSqlSession().close();
        }
        this.holder.reset();
    }
}
3.3 創建新會話
// DefaultSqlSessionFactory
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
        final Environment environment = configuration.getEnvironment();

        // 獲取事務工廠實現
        final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);

        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);

        final Executor executor = configuration.newExecutor(tx, execType);
        return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
        closeTransaction(tx); // may have fetched a connection so lets call close()
        throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
        ErrorContext.instance().reset();
    }
}

private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
    if (environment == null || environment.getTransactionFactory() == null) {
        return new ManagedTransactionFactory();
    }
    return environment.getTransactionFactory();
}

4. 小結

  1. MyBatis 的核心組件 Executor 通過 Transaction 介面來進行事務控制。
  2. 與 Spring 集成時,初始化 Configuration 時會把 transactionFactory 設置為 SpringManagedTransactionFactory 的實例。
  3. 每個 Mapper 代理里注入的 SqlSession 是 SqlSessionTemplate 的實例,其實現了 SqlSession 介面;
  4. SqlSessionTemplate 把對 SqlSession 介面里聲明的方法調用委託給內部的一個動態代理,該代理的方法處理器為內部類 SqlSessionInterceptor 。
  5. SqlSessionInterceptor 接收到方法調用時,通過 SqlSessionUtil 訪問 Spring 的事務設施,如果有與 Spring 當前事務關聯的 SqlSession 則復用;沒有則創建一個。
  6. SqlSessionInterceptor 根據 Spring 當前事務的狀態來決定是否提交或回滾事務。會話的真正關閉是通過註冊在 TransactionSynchronizationManager 上的回調鉤子實現的。

如上圖:
\1. 步驟 1 是 Spring AOP 添加的切面的執行,事務是其中一個切面。
\2. 步驟 2 是 Spring 事務管理機制的部分。
\3. 步驟 3 是業務程式碼,調用 Mapper 代理上的方法。
\4. 步驟 4 是代理上的方法調用被 MapperProxy.invoke 攔截。
\5. 步驟 5、6 是因為 MapperProxy 持有的 sqlSession 是 SqlSessionTemplate,調用到 template 上的方法,又被轉發給內部類 SqlSessionInterceptor ,該類獲得 Spring 事務管理持有的資料庫連接,用以創建 Executor h和 DefaultSqlSession。
\6. 步驟 7 用 DefaultSqlSession 發起資料庫調用。

一級快取和二級快取細節
//www.cnblogs.com/eternal-heathens/p/13366925.html