溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

MyBatis的事務(wù)管理方式

發(fā)布時間:2021-09-04 16:44:56 來源:億速云 閱讀:143 作者:chen 欄目:編程語言

這篇文章主要講解了“MyBatis的事務(wù)管理方式”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“MyBatis的事務(wù)管理方式”吧!

1. 運行環(huán)境 Enviroment

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

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

public class Configuration {
  // 一個 MyBatis 的配置只對應(yīng)一個環(huán)境
  protected Environment environment;
  // 其他屬性 .....
}

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

2. 事務(wù)抽象

MyBatis 把事務(wù)管理抽象出 Transaction 接口,由 TransactionFactory 接口的實現(xiàn)類負責創(chuàng)建。

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 的實現(xiàn)持有一個 SqlSession 實現(xiàn),事務(wù)控制是委托給 SqlSession 的方法來實現(xiàn)的。

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 集成的事務(wù)管理

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();
    }
  }

  // 創(chuàng)建 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);
  }
}

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

3.2 運行時事務(wù)管理

Mapper 的代理對象持有的是 SqlSessionTemplate,其實現(xiàn)了 SqlSession 接口。

SqlSessionTemplate 的方法并不直接調(diào)用具體的 SqlSession 的方法,而是委托給一個動態(tài)代理,通過代理 SqlSessionInterceptor 對方法調(diào)用進行攔截。

SqlSessionInterceptor 負責獲取真實的與數(shù)據(jù)庫關(guān)聯(lián)的 SqlSession 實現(xiàn),并在方法執(zhí)行完后決定提交或回滾事務(wù)、關(guān)閉會話。

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 接口聲明的方法也不少,
    // 在每個方法里添加事務(wù)相關(guān)的攔截比較麻煩,
    // 不如創(chuàng)建一個內(nèi)部的代理對象進行統(tǒng)一處理。
    this.sqlSessionProxy = (SqlSession) newProxyInstance(
      SqlSessionFactory.class.getClassLoader(),
      new Class[] { SqlSession.class },
      new SqlSessionInterceptor());
  }

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

  // 對方法調(diào)用進行攔截,加入事務(wù)控制邏輯
  private class SqlSessionInterceptor implements InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      // 獲取與數(shù)據(jù)庫關(guān)聯(lián)的會話
      SqlSession sqlSession = SqlSessionUtils.getSqlSession(
        SqlSessionTemplate.this.sqlSessionFactory,
        SqlSessionTemplate.this.executorType,
        SqlSessionTemplate.this.exceptionTranslator);
      try {
        // 執(zhí)行 SQL 操作
        Object result = method.invoke(sqlSession, args);
        if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
          // 如果 sqlSession 不是 Spring 管理的,則要自行提交事務(wù)
          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 事務(wù)管理機制的訪問。

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

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

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

  // 注冊到 Spring 的事務(wù)管理機制里
  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);

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

      // 維護會話的引用計數(shù)
      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 管理的不直接關(guān)閉,由回調(diào)鉤子來關(guān)閉
    holder.released();
  } else {
    // 非 Spring 管理的直接關(guān)閉
    session.close();
  }
}

SqlSessionSynchronization 是 SqlSessionUtils 的內(nèi)部私有類,用于作為回調(diào)鉤子與 Spring 的事務(wù)管理機制協(xié)調(diào)工作,TransactionSynchronizationManager 在適當?shù)臅r候回調(diào)其方法。

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;

      // 真正關(guān)閉數(shù)據(jù)庫會話
      this.holder.getSqlSession().close();
    }
  }

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

      // 真正關(guān)閉數(shù)據(jù)庫會話
      this.holder.getSqlSession().close();
    }
    this.holder.reset();
  }
}

3.3 創(chuàng)建新會話

// DefaultSqlSessionFactory
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
  Transaction tx = null;
  try {
    final Environment environment = configuration.getEnvironment();

    // 獲取事務(wù)工廠實現(xiàn)
    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. 小結(jié)

  1. MyBatis 的核心組件 Executor 通過 Transaction 接口來進行事務(wù)控制。

  2. 與 Spring 集成時,初始化 Configuration 時會把 transactionFactory 設(shè)置為 SpringManagedTransactionFactory 的實例。

  3. 每個 Mapper 代理里注入的 SqlSession 是 SqlSessionTemplate 的實例,其實現(xiàn)了 SqlSession 接口;

  4. SqlSessionTemplate 把對 SqlSession 接口里聲明的方法調(diào)用委托給內(nèi)部的一個動態(tài)代理,該代理的方法處理器為內(nèi)部類 SqlSessionInterceptor 。

  5. SqlSessionInterceptor 接收到方法調(diào)用時,通過 SqlSessionUtil 訪問 Spring 的事務(wù)設(shè)施,如果有與 Spring 當前事務(wù)關(guān)聯(lián)的 SqlSession 則復(fù)用;沒有則創(chuàng)建一個。

  6. SqlSessionInterceptor 根據(jù) Spring 當前事務(wù)的狀態(tài)來決定是否提交或回滾事務(wù)。會話的真正關(guān)閉是通過注冊在 TransactionSynchronizationManager 上的回調(diào)鉤子實現(xiàn)的。

感謝各位的閱讀,以上就是“MyBatis的事務(wù)管理方式”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對MyBatis的事務(wù)管理方式這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI