zoukankan      html  css  js  c++  java
  • 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扩展模块时展开。

  • 相关阅读:
    Java实现 蓝桥杯 历届试题 连号区间数
    Java实现 蓝桥杯 历届试题 连号区间数
    Java实现 蓝桥杯 历届试题 连号区间数
    Java实现 蓝桥杯 历届试题 连号区间数
    Java实现 蓝桥杯 历届试题 连号区间数
    Java实现 蓝桥杯 历届试题 大臣的旅费
    Java实现 蓝桥杯 历届试题 大臣的旅费
    Java实现 蓝桥杯 历届试题 大臣的旅费
    Java实现 蓝桥杯 历届试题 大臣的旅费
    Navicat查询哪些表有指定字段名
  • 原文地址:https://www.cnblogs.com/killbug/p/11249097.html
Copyright © 2011-2022 走看看