zoukankan      html  css  js  c++  java
  • Mybatis源码学习之事务管理(八)

    简述

    在实际开发中,数据库事务的控制是一件非常重要的工作,本文将学习Mybatis对事务的管理机制。在Mybatis中基于接口 Transaction 将事务分为两种,一种是JdbcTransaction, 另一种是ManagedTransaction,接下来本文将从源码的角度解读 Transaction 接口的不同实现及其区别。
    image
    MyBatis将事务抽象成 Transaction 接口,该接口包含了数据库事务应有的操作,包括创建(create)、提交(commit)、回滚(rollback)、关闭(close),如下类图

    image

    MyBatis的事务管理分为两种形式:

    一、使用JDBC的事务管理机制:利用java.sql.Connection对象完成对事务的提交(commit())、回滚(rollback())、关闭(close())等

    二、使用MANAGED的事务管理机制:这种机制是让容器如(JBOSS,Weblogic)来实现对事务的管理

    image

    Mybatis事务的使用

    通常我们会通过mybatis.xml配置事务的使用机制,如下 使用JDBC事务管理机制

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
      PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
      "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
      <environments default="development">
        <environment id="development">
          <transactionManager type="JDBC"/>
          <dataSource type="POOLED">
            <property name="driver" value="${driver}"/>
            <property name="url" value="${url}"/>
            <property name="username" value="${username}"/>
            <property name="password" value="${password}"/>
          </dataSource>
        </environment>
      </environments>
    </configuration>

    Mybatis事务工厂创建

    MyBatis事务的创建是交给TransactionFactory 事务工厂来创建的,如果我们将的type 配置为”JDBC”,那么,在MyBatis初始化解析节点时,会根据type=”JDBC”创建一个JdbcTransactionFactory工厂,源码如下:

    /**
         * 解析<transactionManager>节点,创建对应的TransactionFactory
         *
         * @param context xml配置节点信息
         * @return TransactionFactory
         */
        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.");
        }

    如代码所示,如果type = “JDBC”,则MyBatis会创建一个JdbcTransactionFactory.class 实例;如果type=”MANAGED”,则MyBatis会创建一个MangedTransactionFactory.class实例。

    ### Mybatis 事务工厂TransactionFactory
    事务TransactionFactory定义了创建Transaction的两个方法:

    一个是通过指定的Connection对象创建Transaction,

    另外是通过数据源DataSource来创建Transaction。

    与JDBC 和 MANAGED两种Transaction相对应,TransactionFactory有两个对应的实现的子类:

    image

    ### Mybatis 事务创建
    通过上文的学习,很清楚的知道Transaction对象的创建很容易 通过事务工厂TransactionFactory得到。这里我们以JdbcTransaction为例,看一下JdbcTransactionFactory是怎样生成JdbcTransaction的,代码如下:

    /**
     * JdbcTransaction事务工厂,创建JdbcTransaction实例
     *
     * @author kaifeng
     * @author Clinton Begin
     * @see JdbcTransaction
     */
    public class JdbcTransactionFactory implements TransactionFactory {
    
        @Override
        public void setProperties(Properties props) {
        }
    
        /**
         * 根据给定的数据库连接conn创建JdbcTransaction实例
         *
         * @param conn 数据库连接对象
         */
        @Override
        public Transaction newTransaction(Connection conn) {
            return new JdbcTransaction(conn);
        }
    
        /**
         * 根据给定的数据源conn创建JdbcTransaction实例
         *
         * @param ds         数据源
         * @param level      事务隔离级别
         * @param autoCommit 事务是否自动提交
         */
        @Override
        public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
            return new JdbcTransaction(ds, level, autoCommit);
        }
    }
    

    Mybatis JDBC事务

    JdbcTransaction是直接使用JDBC的提交和回滚事务管理机制 。它从dataSource中取得连接connection 来管理transaction 的作用域,connection对象的获取被延迟到调用getConnection()方法。
    JdbcTransaction相当于对java.sql.Connection事务处理进行了包装(wrapper)。

    JdbcTransaction的代码实现如下:

    /**
     * JDBC 事务实现
     *
     * @author kaifeng
     * @author Clinton Begin
     * @see JdbcTransactionFactory
     */
    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) {
            dataSource = ds;
            level = desiredLevel;
            autoCommmit = desiredAutoCommit;
        }
    
        public JdbcTransaction(Connection connection) {
            this.connection = connection;
        }
    
        @Override
        public Connection getConnection() throws SQLException {
            if (connection == null) {
                openConnection();
            }
            return connection;
        }
    
        /**
         * 事务提交,通过connection.commit()
         */
        @Override
        public void commit() throws SQLException {
            if (connection != null && !connection.getAutoCommit()) {
                if (log.isDebugEnabled()) {
                    log.debug("Committing JDBC Connection [" + connection + "]");
                }
                connection.commit();
            }
        }
    
        /**
         * 事务回滚,通过connection.rollback();
         */
        @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();
            }
        }
    
        protected void setDesiredAutoCommit(boolean desiredAutoCommit) {
            try {
                if (connection.getAutoCommit() != desiredAutoCommit) {
                    if (log.isDebugEnabled()) {
                        log.debug("Setting autocommit to " + desiredAutoCommit + " on JDBC Connection [" + connection + "]");
                    }
                    connection.setAutoCommit(desiredAutoCommit);
                }
            } catch (SQLException e) {
                // Only a very poorly implemented driver would fail here,
                // and there's not much we can do about that.
                throw new TransactionException("Error configuring AutoCommit.  "
                        + "Your driver may not support getAutoCommit() or setAutoCommit(). "
                        + "Requested setting: " + desiredAutoCommit + ".  Cause: " + e, e);
            }
        }
    
        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);
                }
            }
        }
    
        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);
        }
    
        @Override
        public Integer getTimeout() throws SQLException {
            return null;
        }
    
    }
    

    Mybatis Managed事务

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

    如下Managed的实现代码:

    /**
     * 让容器来管理事务的整个生命周期,ManagedTransaction的操作不会对数据库产生影响,默认情况下它的数据库连接是关闭的
     *
     * @author kaifeng
     * @author Clinton Begin
     * @see ManagedTransactionFactory
     */
    public class ManagedTransaction implements Transaction {
    
        private static final Log log = LogFactory.getLog(ManagedTransaction.class);
    
        /**
         * 数据源
         */
        private DataSource dataSource;
    
        /**
         * 事务隔离级别
         */
        private TransactionIsolationLevel level;
    
        /**
         * 数据库连接对象
         */
        private Connection connection;
    
        /**
         * 是否关闭数据库连接
         */
        private final 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;
        }
    
        @Override
        public Connection getConnection() throws SQLException {
            if (this.connection == null) {
                openConnection();
            }
            return this.connection;
        }
    
        /**
         * 事务提交,不做任何操作
         */
        @Override
        public void commit() throws SQLException {
            // Does nothing
        }
    
        /**
         * 事务回滚,不做任何操作
         */
        @Override
        public void rollback() throws SQLException {
            // Does nothing
        }
    
        /**
         * 事务关闭,不做任何操作
         */
        @Override
        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());
            }
        }
    
        @Override
        public Integer getTimeout() throws SQLException {
            return null;
        }
    
    }
    

    在实践中,MyBatis通常会与Spring集成使用,数据库的事务是交给Spring进行管理的,在后文我们会介绍Transaction接口的另一个实现—SpringManagedTransaction

  • 相关阅读:
    Web Application Penetration Testing Local File Inclusion (LFI) Testing Techniques
    [Reproduced works]MongoDB Unauthorized Access Vulnerability
    PHP build notes
    两个坑-Linux下Network-Manager有线未托管-DNS resolv.conf文件开机被清空
    OS命令注入中的空格
    Honeywords项目——检查密码是否被破解的一种简单方法
    如何判断自己的VPS是那种虚拟技术实现的
    reGeorg v1.0内网流量转发
    配置OWASP的ModSecurity规则
    python 2.6 与 2.4 区别
  • 原文地址:https://www.cnblogs.com/liukaifeng/p/10052605.html
Copyright © 2011-2022 走看看