Mybatis原理--事务管理

作者: ChinaXieShuai | 来源:发表于2017-09-24 11:45 被阅读123次

    本文将会介绍Mybatis的事务管理机制的原理,首先介绍下事务管理的特质、在Mybatis中是如何创建事务、事务有哪几种类型、不同事务的源码实现、并比较其中的不同、并总结事务的原理。最后分享学习事务常见的小知识。

    事务的概述
    对于数据库事务,具有如下几种特质:
    
    • 创建(create
    • 提交(commit
    • 回滚(rollback
    • 关闭(close
      对应地,MyBatis将事务抽象成了Transaction接口,源码如下:
    Transaction源码
    可以看出,这个接口中包含最基本 的getConnection、commit、rollback、close 方法,任何实现对事物管理都需要实现这几个方法。

    Mybatis中实现事务的管理分为如下两种:

    • JDBC的事务管理机制:即利用java.sql.Connection对象完成对事务的提交(commit())、回滚(rollback())、关闭(close())等
    • MANAGED的事务管理机制:这种机制MyBatis自身不会去实现事务管理,而是让程序的容器如(JBOSSWeblogic)来实现对事务的管理
    事务的创建

    Mybatis在初始化的时候,会加载解析Mybatis的xml配置文件,在xml文件中若配置了事务管理的类型,<transactionManager>type配置为"JDBC",那么,在MyBatis初始化解析<environment>节点时,会根据

    • type="JDBC" 创建一个JdbcTransactionFactory工厂,JdbcTransactionFactory能够创建JDBC类型的事务管理机制
    • type="MANAGED" 创建一个MangedTransactionFactory工厂,MangedTransactionFactory能够创建MANAGED类型的事务管理机制

    源码截图如下:

    Mybatis事务的创建

    下面我们来看看他们的实现细节:

    JdbcTransaction的创建

    JdbcTransactionFactory类会根据 DataSource、隔离级别、是否自动提交 这三个参数创建Transacion,也可以根据给定的数据库连接Connection创建Transaction
    JdbcTransactionFactory 的源码如下:

    public class JdbcTransactionFactory implements TransactionFactory {  
      
      public void setProperties(Properties props) {  
      }  
      
        /** 
         * 根据给定的数据库连接Connection创建Transaction 
         * @param conn Existing database connection 
         * @return 
         */  
      public Transaction newTransaction(Connection conn) {  
        return new JdbcTransaction(conn);  
      }  
      
        /** 
         * 根据DataSource、隔离级别和是否自动提交创建Transacion 
         * 
         * @param ds 
         * @param level Desired isolation level 
         * @param autoCommit Desired autocommit 
         * @return 
         */  
      public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {  
        return new JdbcTransaction(ds, level, autoCommit);  
      }  
    }
    
    JdbcTransaction的实现

    JdbcTransaction直接使用JDBC的提交和回滚事务管理机制 。它依赖与从dataSource中取得的连接connection 来管理transaction 的作用域,connection对象的获取被延迟到调用getConnection()方法。如果autocommit设置为on,开启状态的话,它会忽略commitrollback
    直观地讲,就是JdbcTransaction是使用的java.sql.Connection 上的commitrollback功能,JdbcTransaction只是相当于对java.sql.Connection事务处理进行了一次包装(wrapper),Transaction的事务管理都是通过java.sql.Connection实现的。
    JdbcTransaction的源码如下,快速阅读的读者只需要看本人加注释的部分即可:

    public class JdbcTransaction implements Transaction {
        private static final Log log = LogFactory.getLog(JdbcTransaction.class);
        protected Connection connection;
        protected DataSource dataSource;
        protected TransactionIsolationLevel level;
        protected boolean autoCommmit;
        //根据  DataSource、隔离级别、是否自动提交 三个参数创建Transacion
        public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
            this.dataSource = ds;
            this.level = desiredLevel;
            this.autoCommmit = desiredAutoCommit;
        }
    
        //根据给定的数据库连接Connection创建Transaction
        public JdbcTransaction(Connection connection) {
            this.connection = connection;
        }
    
        public Connection getConnection() throws SQLException {
            if(this.connection == null) {
                this.openConnection();
            }
    
            return this.connection;
        }
    
        //使用 java.sql.Connection 的 commit
        public void commit() throws SQLException {
            if(this.connection != null && !this.connection.getAutoCommit()) {
                if(log.isDebugEnabled()) {
                    log.debug("Committing JDBC Connection [" + this.connection + "]");
                }
    
                this.connection.commit();
            }
    
        }
    
        //使用 java.sql.Connection 的 rollback
        public void rollback() throws SQLException {
            if(this.connection != null && !this.connection.getAutoCommit()) {
                if(log.isDebugEnabled()) {
                    log.debug("Rolling back JDBC Connection [" + this.connection + "]");
                }
    
                this.connection.rollback();
            }
    
        }
        //使用 java.sql.Connection 的 close
        public void close() throws SQLException {
            if(this.connection != null) {
                this.resetAutoCommit();
                if(log.isDebugEnabled()) {
                    log.debug("Closing JDBC Connection [" + this.connection + "]");
                }
    
                this.connection.close();
            }
    
        }
    
        protected void setDesiredAutoCommit(boolean desiredAutoCommit) {
            try {
                if(this.connection.getAutoCommit() != desiredAutoCommit) {
                    if(log.isDebugEnabled()) {
                        log.debug("Setting autocommit to " + desiredAutoCommit + " on JDBC Connection [" + this.connection + "]");
                    }
    
                    this.connection.setAutoCommit(desiredAutoCommit);
                }
    
            } catch (SQLException var3) {
                throw new TransactionException("Error configuring AutoCommit.  Your driver may not support getAutoCommit() or setAutoCommit(). Requested setting: " + desiredAutoCommit + ".  Cause: " + var3, var3);
            }
        }
    
        protected void resetAutoCommit() {
            try {
                if(!this.connection.getAutoCommit()) {
                    if(log.isDebugEnabled()) {
                        log.debug("Resetting autocommit to true on JDBC Connection [" + this.connection + "]");
                    }
    
                    this.connection.setAutoCommit(true);
                }
            } catch (SQLException var2) {
                if(log.isDebugEnabled()) {
                    log.debug("Error resetting autocommit to true before closing the connection.  Cause: " + var2);
                }
            }
    
        }
    
        protected void openConnection() throws SQLException {
            if(log.isDebugEnabled()) {
                log.debug("Opening JDBC Connection");
            }
    
            this.connection = this.dataSource.getConnection();
            if(this.level != null) {
                this.connection.setTransactionIsolation(this.level.getLevel());
            }
    
            this.setDesiredAutoCommit(this.autoCommmit);
        }
    
        public Integer getTimeout() throws SQLException {
            return null;
        }
    }
    
    总结:
    • 当使用DataSource创建数据库连接时,数据库的事务隔离级别使用DataSource默认的事务隔离级别
    • 当使用 DataSource、隔离级别、是否自动提交 三个参数创建JdbcTransaction时,会使用传入的参数来设定隔离级别和是否自动提交
    • 对select不进行事务控制
    • JdbcTransaction的事务,原理上就是封装了一层JDBC的方法

    ManagedTransaction的实现

    ManagedTransaction让容器来管理事务Transaction的整个生命周期,使用ManagedTransactioncommitrollback功能不会对事务有任何的影响,它什么都不会做,它将事务管理的权利移交给了容器来实现

    public class ManagedTransaction implements Transaction {
        private static final Log log = LogFactory.getLog(ManagedTransaction.class);
        private DataSource dataSource;
        private TransactionIsolationLevel level;
        private Connection connection;
        private boolean closeConnection;
    
        public ManagedTransaction(Connection connection, boolean closeConnection) {
            this.connection = connection;
            this.closeConnection = closeConnection;
        }
    
        public ManagedTransaction(DataSource ds, TransactionIsolationLevel level, boolean closeConnection) {
            this.dataSource = ds;
            this.level = level;
            this.closeConnection = closeConnection;
        }
    
        public Connection getConnection() throws SQLException {
            if(this.connection == null) {
                this.openConnection();
            }
    
            return this.connection;
        }
        //不做任何处理
        public void commit() throws SQLException {
        }
        //不做任何处理
        public void rollback() throws SQLException {
        }
    
        public void close() throws SQLException {
            if(this.closeConnection && this.connection != null) {
                if(log.isDebugEnabled()) {
                    log.debug("Closing JDBC Connection [" + this.connection + "]");
                }
    
                this.connection.close();
            }
    
        }
    
        protected void openConnection() throws SQLException {
            if(log.isDebugEnabled()) {
                log.debug("Opening JDBC Connection");
            }
    
            this.connection = this.dataSource.getConnection();
            if(this.level != null) {
                this.connection.setTransactionIsolation(this.level.getLevel());
            }
    
        }
    
        public Integer getTimeout() throws SQLException {
            return null;
        }
    }
    
    关于事务的隔离级别
    先对不同隔离级别涉及到的名词解释:

    脏读: 对于两个事物 T1、T2,T1 读取了已经被 T2 更新但还没有被提交的字段。之后, 若 T2 回滚,T1读取的内容就是临时且无效的。
    不可重复读: 对于两个事物 T1、T2, T1 读取了一个字段, 然后 T2 更新了该字段。 之后, T1再次读取同一个字段, 值就不同了。
    幻读: 对于两个事物 T1、T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入了一些新的行。 如果 T1 再次读取同一个表, 就会多出数据

    具体的隔离级别定义:
    • READ UNCOMMITTED(读未提交数据) :允许事务读取未被其他事务提交的变更,脏读、不可重复读和幻读的问题都会出现
    • READ COMMITED(读已提交数据) :只允许事务读取已经被其他事务提交的变更,可以避免脏读,但不可重复读和幻读问题仍然会出现
    • REPEATABLE READ(可重复读) :确保事务可以多次从一个字段中读取相同的值,在这个事务持续期间,禁止其他事务对这个字段进行更新,可以避免脏读和不可重复读,但幻读的问题依然存在
    • SERIALIZABLE(串行化) :确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作,所有并发问题都可以避免,但性能十分低

    Oracle 默认的事务隔离级别为: READ COMMITED(读已提交数据)
    Mysql 默认的事务隔离级别为: REPEATABLE READ(可重复读)

    以上就是《Mybatis原理--事务管理》的全部内容,如有不正确的地方,请读者指正,互相学习,共同进步,谢谢。

    相关文章

      网友评论

        本文标题:Mybatis原理--事务管理

        本文链接:https://www.haomeiwen.com/subject/fuhkextx.html