转载

MyBatis 的秘密(一)Transaction

public List<Country> selectById()throws IOException {
    try (Reader reader = Resources.getResourceAsReader("mybatis-config.xml")) {
        //创建SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        //获取SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //执行Sql
        return sqlSession.selectList("selectAll");
    }
}

上面是使用 MyBatis 执行一段 SQL 完整的 Java API

可以看到整个过程中创建了4个对象:

  • Reader : 用来读取 MyBatis 的配置文件流
  • SqlSessionFactory : 用来根据各种配置创建 SqlSession ,一般应该作为 static 成员变量存在,只初始化一次
  • SqlSession : 真正的干活的类

对于 SqlSessionFactoryBuilder.builder() ,这里只用简单说说,此类是用来读取 MyBatisXML 配置文件,然后根据配置文件来初始化 Configuration 对象,目前只用知道这个,当需要了解细节的时候再细看。

因此,对于 MyBatis 的秘密,全都藏在了 SqlSession 中。

首先看看 SqlSession 的创建方法: SqlSessionFactory#openSession()

由官方文档中可以知道, openSession() 包括以下几种重载:

//默认,level 使用数据库默认,autoCommit 为false ,execType 为defaultExecType 默认为SIMPLE
//通过配置的DataSource 获取Connection 对象
SqlSession openSession()
//指定是否自动提交
SqlSession openSession(boolean autoCommit)
//通过connection 初始化
SqlSession openSession(Connection connection)
//指定事务级别    
SqlSession openSession(TransactionIsolationLevel level)
//指定执行器类型和事务级别    
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level)
SqlSession openSession(ExecutorType execType)
SqlSession openSession(ExecutorType execType, boolean autoCommit)
//通过connection初始化,并指定事务级别    
SqlSession openSession(ExecutorType execType, Connection connection)

也就是说,对于 SqlSession ,我们可以直接配置的属性有:

  • ExecutorType : 执行器类型
  • AutoCommit : 是否开启事务 ( false 为开, true 为不开)
  • TransactionIsolationLevel : 事务级别

接下来看看创建 SqlSession 的代码:

SqlSessionFactory#openSessionFromDataSource()

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
        //获取配置文件的environment对象
        final Environment environment = configuration.getEnvironment();
        //从environment对象中获取事务管理器创建对象
        final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
        //创建事务管理器
        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        //创建执行器
        final Executor executor = configuration.newExecutor(tx, execType);
        //将执行器作为参数创建SqlSession
        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();
    }
}

总结起来分为3步:

  • 获取 environment 对象
  • 根据 environment 对象创建事务管理器工厂以及通过工厂创建 Transaction
  • Transaction 对象作为参数创建执行器
  • 将执行器作为 SqlSession 构造参数创建 SqlSession

首先我们看 environment 的配置:

<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://127.0.0.1:3306/new_test"/>
            <property name="username" value="root"/>
            <property name="password" value="123456"/>
        </dataSource>
    </environment>
</environments>

这里配置了两个元素: transactionManager 以及 dataSource

Transaction

这个类算是 MyBaits 中比较重要的一个类,重要在哪里呢?它的实现特别简单,但是在 MyBatis 中起着一个中间层的作用,首先看这个接口包含的方法:

public interface Transaction {

  Connection getConnection() throws SQLException;

  void commit() throws SQLException;

  void rollback() throws SQLException;

  //....
}

可以看出来这些功能其实就是对 Connection 的包装。

MyBatis 中, Transaction 有两个实现类: JdbcTransactionManagedTransaction

对于 JdbcTransaction 来说,其相当于 connection 的代理类,需要提交的时候便调用 Connection 的提交,需要回滚的时候便调用 Connection 的回滚方法

而对于 ManagedTransaction 来说,缺忽略了 commit()rollback() 方法,其实现类中什么都没做。

MyBatis 的官方文档中,写明了:

  • JDBC – 这个配置就是直接使用了 JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。
  • MANAGED – 这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)

看到这里就大概能明白 MyBatis 设计 Transaction 中间层的意义了,某些时候,当 MyBatis 和其他类似 EJB 的服务整合的时候,此时不应该在 MyBatis 中涉及到提交或者回滚,而应该交由 EJB 容器管理,而这个中间层则正好可以做到一个动态选择的作用,这样可以在不修改任何代码的情况下,实现由程序本身控制事务还是交给容器管理的选择。

同理,可以想到 Spring 整合 MyBatis ,在 Spring 中,定义了事务的传播机制,而这些机制的实现,依然需要依赖 Transaction 做中间层代理,这样使用 Transaction 的代码都不用动,而实现 Spring 的事务传播则交给 Transaction 来做。

Spring-MyBatis 中,实现此接口的类为: SpringManagedTransaction

从这里便可以看出来面向对象的抽象的好处,如果简单的设计,可能会使得代码中到处都是 if-else 的执行判断

熟悉了 Transaction 后,应该明白它充当的作用便是 Connection 的作用,下一章来看 Executor

原文  http://dengchengchao.com/?p=1188
正文到此结束
Loading...