Mybatis 源码分析:事物管理

1. mybatis 事物概述

mybatis 使用 Transaction 接口封装了数据库连接 Connection 的生命周期,它由 creation、preparation、commit/rollback 和 close 组成。

public interface Transaction {
	Connection getConnection() throws SQLException;

  	void commit() throws SQLException;

  	void rollback() throws SQLException;

  	void close() throws SQLException;
}

mybatis 使用两种机制来管理事物:

  • 使用 JDBC 的事务管理机制:

    即利用 java 提供的 Connection 对象完成对事务的 commit、rollback 和 close。

  • 使用 MANAGED 的事务管理机制

    这种机制 myBatis 自身不会去实现事务管理,而是让程序的容器(如 JBOSS,Spring)来实现对事务的管理。

Transaction

2. mybatis 事物原理

mybatis 会根据如下配置来决定使用何种方式管理数据库事物。

<configuration>
	<environment>
		<transactionManager type="JDBC" />
	</environment>
</configuration>

因此,mybatis 在解析配置文件 <environment /> 元素时,会生成具体的 TransactionFactory 对象。

private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
      if (environment == null) {
        environment = context.getStringAttribute("default");
      }
      for (XNode child : context.getChildren()) {
        String id = child.getStringAttribute("id");
        if (isSpecifiedEnvironment(id)) {
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
          DataSource dataSource = dsFactory.getDataSource();
          Environment.Builder environmentBuilder = new Environment.Builder(id)
              .transactionFactory(txFactory)
              .dataSource(dataSource);
          configuration.setEnvironment(environmentBuilder.build());
        }
      }
    }
 }
private TransactionFactory transactionManagerElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type");
      Properties props = context.getChildrenAsProperties();
      // 获取具体的 事物工厂
      TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
      factory.setProperties(props);
      return factory;
    }
    throw new BuilderException("Environment declaration requires a TransactionFactory.");
  }

在上面提到,mybatis 提供两种机制来管理事物:JdbcTransaction 和 ManagedTransaction。这可以看成是两种类型产品,因此 mybatis 使用了抽象工厂模式来根据不同的配置来创建具体的事物管理机制。
TransactionFactory
但是现在还只是得到能够生产 Transaction 的工厂,那么 mybatis 是从哪开始通过 TransactionFactory 来具体构建事物管理对象呢?由于 mybatis 执行数据库操作都是通过 SqlSession 来的,那么可以推测出应该是在得到 SqlSession 对象时,把这些东西都准备好。那就看看 DefaultSqlSession 的 openSession() 方法是不是跟推测的一样。

@Override
public SqlSession openSession() {
	// 默认 executor 是 SimpleExecutor,且默认 autoCommit 为 false
	return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      // 从 Environment 对象中获取之前创建好的 TransactionFactory
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      // 关键:生成 Transaction 对象
      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();
    }
  }

我们已 JdbcTransactionFactory 为例,因为 ManagedTransactionFactory 生成的 Transaction 并不会做 commit 和 rollback 操作,它会交由容器去管理,如 Spring。

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

目前,我们已经知道 mybatis 是通过什么来管理事物,那么在我们具体调用 SqlSession 对象的 update 或 delete 操作它又是如何工作的呢?

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

可以看到,update 操作会设置 dirty 为 true,那这个变量又有什么用?我们可以看看 commit() 和 rollback() 方法。
PS:delete 操作其实底层调用的是 update 方法

@Override
public void commit() {
	commit(false);
}
@Override
public void commit(boolean force) {
	try {
	  executor.commit(isCommitOrRollbackRequired(force));
	  dirty = false;
	} catch (Exception e) {
	  throw ExceptionFactory.wrapException("Error committing transaction.  Cause: " + e, e);
	} finally {
	  ErrorContext.instance().reset();
	}
}
private boolean isCommitOrRollbackRequired(boolean force) {
	return (!autoCommit && dirty) || force;
}

在这个方法中,我们可以看到 mybatis 是通过使用三个值来决定是否需要 commit 或 rollback。默认情况下 force 为 false,而 dirty 肯定为 true,因此只要 autoCommit 为 false,就需要 commit 或 rollback,否则不需要。
rollback 的原理跟 commit 是一样的,就不再赘述了~~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值