当前位置 主页 > 网站技术 > 代码类 >

    浅谈MyBatis 事务管理

    栏目:代码类 时间:2019-10-27 12:08

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