Mybatis原理--事务管理

本文将会介绍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原理--事务管理》的全部内容,如有不正确的地方,请读者指正,互相学习,共同进步,谢谢。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345

推荐阅读更多精彩内容