• 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扩展模块时展开。

  • 相关阅读:
    Liunx学习总结(三)--用户和用户组管理
    Liunx学习总结(二)--目录和文件管理
    Markdown表格宽度调整
    Liunx学习总结(一)--初识 Liunx
    好看的404页面
    qt打印输出到控制台
    平台 DllRegisterServer调用失败
    linux下编译安装SDL2和ffmpeg
    ffmpeg函数05__vcodec_decode_video2()
    ffmpeg函数04__v_register_output_format()
  • 原文地址:https://www.cnblogs.com/killbug/p/11249097.html
Copyright © 2020-2023  润新知