Transaction-Mybatis源码

Posted killbug

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Transaction-Mybatis源码相关的知识,希望对你有一定的参考价值。

github地址:https://github.com/dchack/Mybatis-source-code-learn (欢迎star)

TransactionFactory

官方文档:

在 MyBatis 中有两种类型的事务管理器(也就是 type=”[JDBC|MANAGED]”):
JDBC – 这个配置就是直接使用了 JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。
MANAGED – 这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将 closeConnection 属性设置为 false 来阻止它默认的关闭行为。例如:

<transactionManager type="MANAGED">
 <property name="closeConnection" value="false"/>
</transactionManager>

提示如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器, 因为 Spring 模块会使用自带的管理器来覆盖前面的配置。

以上配置transactionManager属性来配置使用哪一种TransactionFactory的代码,肯定在MybatisXMLConfigBuilder中可以找到:

    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.");
    

TransactionFactory入手:

public interface TransactionFactory 

  /**
   * Sets transaction factory custom properties.
   * @param props
   */
  void setProperties(Properties props);

  /**
   * Creates a @link Transaction out of an existing connection.
   * @param conn Existing database connection
   * @return Transaction
   * @since 3.1.0
   */
  Transaction newTransaction(Connection conn);
  
  /**
   * Creates a @link Transaction out of a datasource.
   * @param dataSource DataSource to take the connection from
   * @param level Desired isolation level
   * @param autoCommit Desired autocommit
   * @return Transaction
   * @since 3.1.0
   */
  Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);

TransactionFactory接口描述实现者需要从ConnectionDataSource生产org.apache.ibatis.transaction.Transaction出来。

接口实现类如下:
技术图片

他们各自生产的Transaction分别是:

  • JdbcTransaction
  • ManagedTransaction
  • SpringManagedTransaction

Transaction接口:

/**
 * Wraps a database connection.
 * Handles the connection lifecycle that comprises: its creation, preparation, commit/rollback and close. 
 *
 * @author Clinton Begin
 */
public interface Transaction 

  /**
   * Retrieve inner database connection
   * @return DataBase connection
   * @throws SQLException
   */
  Connection getConnection() throws SQLException;

  /**
   * Commit inner database connection.
   * @throws SQLException
   */
  void commit() throws SQLException;

  /**
   * Rollback inner database connection.
   * @throws SQLException
   */
  void rollback() throws SQLException;

  /**
   * Close inner database connection.
   * @throws SQLException
   */
  void close() throws SQLException;

  /**
   * Get transaction timeout if set
   * @throws SQLException
   */
  Integer getTimeout() throws SQLException;
  

抽象出了控制connection生命周期的核心接口:getConnection(create),commit,rollback,close

JdbcTransaction的实现:

三个操作方法:commit,rollback,close,都是connection的封装而已,commit,rollback执行的条件需要已经生成好connection并且AutoCommit没有设置true,close方法会调用resetAutoCommit方法重置ConnectionautoCommit属性为true

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

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

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

重置autoCommit属性方法:

protected void resetAutoCommit() 
    try 
      if (!connection.getAutoCommit()) 
        // MyBatis does not call commit/rollback on a connection if just selects were performed.
        // Some databases start transactions with select statements
        // and they mandate a commit/rollback before closing the connection.
        // A workaround is setting the autocommit to true before closing the connection.
        // Sybase throws an exception here.
        if (log.isDebugEnabled()) 
          log.debug("Resetting autocommit to true on JDBC Connection [" + connection + "]");
        
        connection.setAutoCommit(true);
      
     catch (SQLException e) 
      if (log.isDebugEnabled()) 
        log.debug("Error resetting autocommit to true "
          + "before closing the connection.  Cause: " + e);
      
    
  

在看下getConnection方法的实现:

  @Override
  public Connection getConnection() throws SQLException 
    if (connection == null) 
      openConnection();
    
    return connection;
  
  protected void openConnection() throws SQLException 
    if (log.isDebugEnabled()) 
      log.debug("Opening JDBC Connection");
    
    connection = dataSource.getConnection();
    if (level != null) 
      connection.setTransactionIsolation(level.getLevel());
    
    setDesiredAutoCommit(autoCommmit);
  

openConnection中设置了事务隔离级别(transaction isolation level)和autoCommmit。

事务隔离级别在TransactionIsolationLevel枚举中可以看到:

public enum TransactionIsolationLevel 
  NONE(Connection.TRANSACTION_NONE),
  READ_COMMITTED(Connection.TRANSACTION_READ_COMMITTED),
  READ_UNCOMMITTED(Connection.TRANSACTION_READ_UNCOMMITTED),
  REPEATABLE_READ(Connection.TRANSACTION_REPEATABLE_READ),
  SERIALIZABLE(Connection.TRANSACTION_SERIALIZABLE);

  private final int level;

  private TransactionIsolationLevel(int level) 
    this.level = level;
  

  public int getLevel() 
    return level;
  

java.sql.Connection中的定义和注释如下:

/**
     * A constant indicating that transactions are not supported.
     */
    int TRANSACTION_NONE             = 0;

    /**
     * A constant indicating that
     * dirty reads, non-repeatable reads and phantom reads can occur.
     * This level allows a row changed by one transaction to be read
     * by another transaction before any changes in that row have been
     * committed (a "dirty read").  If any of the changes are rolled back,
     * the second transaction will have retrieved an invalid row.
     */
    int TRANSACTION_READ_UNCOMMITTED = 1;

    /**
     * A constant indicating that
     * dirty reads are prevented; non-repeatable reads and phantom
     * reads can occur.  This level only prohibits a transaction
     * from reading a row with uncommitted changes in it.
     */
    int TRANSACTION_READ_COMMITTED   = 2;

    /**
     * A constant indicating that
     * dirty reads and non-repeatable reads are prevented; phantom
     * reads can occur.  This level prohibits a transaction from
     * reading a row with uncommitted changes in it, and it also
     * prohibits the situation where one transaction reads a row,
     * a second transaction alters the row, and the first transaction
     * rereads the row, getting different values the second time
     * (a "non-repeatable read").
     */
    int TRANSACTION_REPEATABLE_READ  = 4;

    /**
     * A constant indicating that
     * dirty reads, non-repeatable reads and phantom reads are prevented.
     * This level includes the prohibitions in
     * <code>TRANSACTION_REPEATABLE_READ</code> and further prohibits the
     * situation where one transaction reads all rows that satisfy
     * a <code>WHERE</code> condition, a second transaction inserts a row that
     * satisfies that <code>WHERE</code> condition, and the first transaction
     * rereads for the same condition, retrieving the additional
     * "phantom" row in the second read.
     */
    int TRANSACTION_SERIALIZABLE     = 8;
关于事务隔离级别

几个概念:

  • 脏读:读取的数据可以取到其他未提交事务修改的数据
  • 不可重复读:一个事务中多次读取相同的数据,因其他事务在中间修改了这个数据,导致第一个事务多次读的数据会不相同
  • 幻读:就是在一个事务提交时发现之前查的条件发生了改变

隔离级别:

  • 提交读(READ_COMMITTED)只能读取到已经提交的数据
  • 未提交读(READ_UNCOMMITTED)允许脏读
  • 可重复读(REPEATABLE_READ)在同一事务中保证多次读取的数据是一致的
  • 串行读(SERIALIZABLE)每次读都需要获取表级锁,读写互相阻塞

mysql中查看隔离级别设置:

select @@global.tx_isolation;

另外我们也看到JdbcTransaction中是需要autoCommmit设置true的,否则是不能完成事务功能的。

ManagedTransaction

从类注释上可以看到:ManagedTransaction是将事务的生命周期交给容器管理,可以理解它都是空实现,比如commit,rollbackclose可以通过closeConnection字段来关闭。

SpringManagedTransaction

后续进入Mybatis扩展模块时展开。

以上是关于Transaction-Mybatis源码的主要内容,如果未能解决你的问题,请参考以下文章

Mybatis全方位深入源码剖析

MyBatis源码分析-MyBatis初始化流程

MyBatis源码分析-SQL语句执行的完整流程

Mybatis框架分析

免费分享20套微信小程序源码 源码免费下载强烈推荐

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.myba