Mybatis事务管理

Mybatis事务管理

  在上一篇写了简单的SSM框架搭建相关链接,虽然简单快速,但是并不能真正的运用到实际的项目中,还有许多实际问题需要考虑,在接下来的几篇文章中我回讲解SSM中一些核心技术(我认为的核心技术)的讲解(其实就是学习笔记的啦),有什么不足或者错误的地方还请高手斧正!

  我们会在接下来的两篇文章中分别讲解Mybatis和Spring
的事务管理。在学习事务管理之前,需要对数据库事务的知识有一定的掌握,例如:事务的定义、隔离级别、锁等概念,我把一些网上的概念性的文字材料进行一个整理,我会把几个参考资料链接放在文末,如果大家对数据库的这些知识还不是太了解,请直接移至结尾。

  从这里开始默认已经掌握了资料里的事务相关的知识,其概念和特性也就不再一一描述,直接切入正题来讲解Mybatis的事务处理。

Mybatis的事务管理

  Mybatis的事务管理一般在单独使用Mybatis时才会使用,在SSM整合之后,一般都是用Spring的事务管理,虽然我们使用的是SSM框架,但还是有必要详细的了解一下Mybatis的事务管理。

  本文没有太多的应用代码,大部分都是对Mybatis接口的分析,文中的代码都能在mybatis-X.X.X.jar中找到。

Transaction接口

  Mybatis的事务管理主要是由org.apache.ibatis.transaction.Transaction接口,以及他的两个实现类org.apache.ibatis.transaction.managed.ManagedTransactionorg.apache.ibatis.transaction.jdbc.JdbcTransaction。除此之外,Mybatis中还有另外一个接口org.apache.ibatis.transaction.TransactionFactory和两个实现类org.apache.ibatis.transaction.jdbc.JdbcTransactionFactoryorg.apache.ibatis.transaction.managed.ManagedTransactionFactory。代码不一一贴出,可以到mybatis-X.X.X.jar下,很轻松就能找到(如果你要是不知道mybatis-X.X.X.jar是什么,那你最好认真的学习Mybatis基础后在进行深入的学习)。
接下来看重点:Transaction接口。

... ...
public interface Transaction {
    //获取数据库链接
    Connection getConnection() throws SQLException;
    //提交事务
    void commit() throws SQLException;
    //回滚事务
    void rollback() throws SQLException;
    //关闭数据库连接
    void close() throws SQLException;
}

  大家可以发现,不管是Transaction接口的实现类还是TransactionFactory接口的实现类,前缀都分为了Jdbc和Managed两个。这就引出Mybatis事务管理的两种机制:

  • JDBC事务管理机制。
  • MANAGED事务管理机制。

隔离级别的定义

  相对于数据库的四种隔离级别,在Mybatis的隔离级别中,多定义了一个没有事务(NONE),其余的和数据库的四种隔离级别均相同。

... ...
public enum TransactionIsolationLevel {
    //没有事务
    NONE(0),
    //已提交读(一般数据库中的默认隔离级别)
    READ_COMMITTED(2),
    //未提交读
    READ_UNCOMMITTED(1),
    //可重复读
    REPEATABLE_READ(4),
    //直译为序列化
    SERIALIZABLE(8);

    private final int level;

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

    public int getLevel() {
        return this.level;
    }
}

事务配置

  对于Mybatis事务的配置,一般放在Mybatis的配置文件mybatis-config.xml中。其中定义的信息如下:

 <!--Mybatis的事务管理,注意配置的顺序也是一门学问,environments的配置要在typeAliases之下,具体顺序看提示-->
    <environments default="development">
        <environment id="mysql">
            <!--type决定事务管理机制-->
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://localhost:3306/javafeng" />
                <property name="username" value="root" />
                <property name="password" value="root" />
            </dataSource>
        </environment>
    </environments>

  元素用来定义某数据库的信息,元素的type属性决定事务管理的机制(上述的JDBC和MANAGED)。在mybatis的配置文件中,对于集中元素的配置顺序有一定的要求,顺序出错的话配置文件就会报错,可以按照提示来调整。
这里写图片描述

事务工厂

  Mybatis的事务创建交给事务工厂来完成,Mybatis在初始化时会根据节点的type属性来注册相应的事务工厂。

创建

   private  TransactionFactory transationManagerElement(XNode context) throws Exception
   {
       /*
       Configuration在初始化的时候会根据transactionManager节点的type属性来创建相应的事务工厂
       typeAliaRegistry.registerAlias("JDBC",JdbcTransactionFactory.class);
       typeAliaRegistry.registerAlias("MANAGED",ManagedTransactionFactory.class);
        */
       if(context != null)
       {
           String type = context.getStringAttribute("type");
           Properties props = context.getChildrenAsProperties();
           //从类型解决器中获取type对应的类型的实例,即JdbcTransactionFactory
           TransactionFactory factory = (TransactionFactory)resolveClass(type).newInstance();
           //设置JdbcTransactionFactory属性
           factory.setProperties(props);
           return factory;
       } else
       {
            //抛出异常
           throw new BuilderException("Environment declaration requires a TransactionFactory.");
       }
   }

详解

  我们可以很容易的从事务工厂TransactionFactory中得到Transaction对象实例。接下来我们以JdbcTransaction的生成为例来看一下。直接上代码

... ...
public class JdbcTransactionFactory implements TransactionFactory {
    public JdbcTransactionFactory() {
    }

    public void setProperties(Properties props) {
    }
    //通过指定的数据库链接(Connection)来创建事务(Transaction)
    public Transaction newTransaction(Connection conn) {
        return new JdbcTransaction(conn);
    }
    //通过数据源(DataSource),隔离级别(TransactionIsolationLevel)和是否自动提交来创建事务(Transaction)
    public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
        return new JdbcTransaction(ds, level, autoCommit);
    }
}

  如上就是JdbcTransactionFactory创建JDBC类型Transaction(也就是JdbcTransaction)的过程,这里只是JdbcTransaction的示例,ManagedTransactionFactory则会创建ManagedTransaction。
  接下来就来看这两种机制的具体实现。
1. JdbcTransaction
  JdbcTransaction直接使用JDBC的提交和回滚事务管理机制,它对于事务的实现依赖从数据源(DataSource)中获取的连接来管理事务,在调用getConnection()时才会取得connection对象。如果其自动提交属性(autocommit)被设置为开启状态(on)时,它会自动忽略提交(commit)和回滚(rollback)。并且它的事务操作都是使用java.sql.Connection上的提交(commit)和回滚(rollback)功能来完成的。JdbcTransaction相当于对java.sql.Connection事务处理的重新封装,事务管理均由java.sql.Connection完成。上代码

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;

    public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
        this.dataSource = ds;
        this.level = desiredLevel;
        this.autoCommmit = desiredAutoCommit;
    }

    public JdbcTransaction(Connection connection) {
        this.connection = connection;
    }

    public Connection getConnection() throws SQLException {
        if(this.connection == null) {
            this.openConnection();
        }

        return this.connection;
    }

    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();
        }

    }

    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();
        }

    }

    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) {
            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);
    }
}

  如上可以看到,所有的实务操作均由connection对象来完成。

  1. ManagedTransaction
      ManagedTransaction和JdbcTransaction就有很大的区别了,实际上ManagedTransaction对事物的处理任务全部交给了容器来完成(吐槽:万能背锅侠,啥啥都能干),即使调用了ManagedTransaction的提交(commit)和回滚(rollback),他都不会进行任何操作。上代码
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());
        }

    }
}

  如上可以看到,它的commit()和rollback()都nothing(对就是nothing,没有任何操作),正因为它吧整个事务的整个生命周期都交给容器,因此他不需要做任何操作。但它在默认情况下是可以关闭一个链接的,当然我们也可以通过设置closeConnection为false来让它不能关闭连接。

小结

  1. Transaction接口和他的一系列实现类。
  2. 事务工厂TransactionFactory。
  3. JdbcTransaction和ManagedTransaction解析。

参考资料

Mybatis的environments和properties

百度百科:数据库事务

MySQL 四种事务隔离级的说明

微软teachnet教程:锁模式

以上链接版权属作者所有,在此不一一鸣谢!

posted @ 2017-08-01 11:08  翟亚峰  阅读(51)  评论(0)    收藏  举报