1 Star 0 Fork 165

shk / Java-Review

forked from icanci / Java-Review 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
SSM-Mybatis3-源码解读-第7篇-事务模块.md 16.13 KB
一键复制 编辑 原始数据 按行查看 历史
icanci 提交于 2020-09-07 10:22 . :zap:更新文件名称

SSM - Mybatis3 - 源码解读 - 第7篇 - 事务模块

概述

  • 事务模块对应 transaction
  • Mybatis对数据库中的事务进行了抽象,其自身提供了 相应的事务接口和和简单实现
  • 在很多场景中,Mybatis会和Spring框架集成,并由 Spring框架管理事务
  • 类图

  • 整体包的结构

1599183778861

Transaction

  • org.apache.ibatis.transaction.Transaction ,事务接口
public interface Transaction {

    // 获取连接
    Connection getConnection() throws SQLException;

    // 提交事务
    void commit() throws SQLException;

    // 回滚事务
    void rollback() throws SQLException;

    // 关闭连接
    void close() throws SQLException;

    // 获得事务超时时间
    Integer getTimeout() throws SQLException;

}
JdbcTransaction
  • org.apache.ibatis.transaction.jdbc.JdbcTransaction ,实现 Transaction 接口,基于 JDBC 的事务实现类
public class JdbcTransaction implements Transaction {

    private static final Log log = LogFactory.getLog(JdbcTransaction.class);

    // Connection 对象
    protected Connection connection;
    // DataSource 数据源对象
    protected DataSource dataSource;
    // 事务的隔离级别
    protected TransactionIsolationLevel level;
    // 是否自动提交
    protected boolean autoCommit;

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

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

    @Override
    public Connection getConnection() throws SQLException {
        // 连接为空,执行创建
        if (connection == null) {
            openConnection();
        }
        return connection;
    }

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

    // 重置 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);
            }
        }
    }

    // 获得 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(autoCommit);
    }

    @Override
    public Integer getTimeout() throws SQLException {
        return null;
    }

}
ManagedTransaction
  • org.apache.ibatis.transaction.managed.ManagedTransaction ,实现 Transaction 接口,基于容器管理的事务实现类
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;
    }

}
SpringManagedTransaction
  • org.mybatis.spring.transaction.SpringManagedTransaction ,实现 Transaction 接口,基于 Spring 管理的事务实现类。实际真正在使用的,SpringManagedTransaction
  • 源码如下,其实很类似
/**
 *    Copyright 2010-2015 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.mybatis.spring.transaction;

import static org.springframework.util.Assert.notNull;

import java.sql.Connection;
import java.sql.SQLException;

import javax.sql.DataSource;

import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.transaction.Transaction;
import org.springframework.jdbc.datasource.DataSourceUtils;

/**
 * {@code SpringManagedTransaction} handles the lifecycle of a JDBC connection.
 * It retrieves a connection from Spring's transaction manager and returns it back to it
 * when it is no longer needed.
 * <p>
 * If Spring's transaction handling is active it will no-op all commit/rollback/close calls
 * assuming that the Spring transaction manager will do the job.
 * <p>
 * If it is not it will behave like {@code JdbcTransaction}.
 *
 * @author Hunter Presnall
 * @author Eduardo Macarron
 * 
 * @version $Id$
 */
public class SpringManagedTransaction implements Transaction {

    private static final Log LOGGER = LogFactory.getLog(SpringManagedTransaction.class);

    private final DataSource dataSource;

    private Connection connection;

    private boolean isConnectionTransactional;

    private boolean autoCommit;

    public SpringManagedTransaction(DataSource dataSource) {
        notNull(dataSource, "No DataSource specified");
        this.dataSource = dataSource;
    }

    /**
   * {@inheritDoc}
   */
    @Override
    public Connection getConnection() throws SQLException {
        if (this.connection == null) {
            openConnection();
        }
        return this.connection;
    }

    /**
   * Gets a connection from Spring transaction manager and discovers if this
   * {@code Transaction} should manage connection or let it to Spring.
   * <p>
   * It also reads autocommit setting because when using Spring Transaction MyBatis
   * thinks that autocommit is always false and will always call commit/rollback
   * so we need to no-op that calls.
   */
    private void openConnection() throws SQLException {
        this.connection = DataSourceUtils.getConnection(this.dataSource);
        this.autoCommit = this.connection.getAutoCommit();
        this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug(
                "JDBC Connection ["
                + this.connection
                + "] will"
                + (this.isConnectionTransactional ? " " : " not ")
                + "be managed by Spring");
        }
    }

    /**
   * {@inheritDoc}
   */
    @Override
    public void commit() throws SQLException {
        if (this.connection != null && !this.isConnectionTransactional && !this.autoCommit) {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Committing JDBC Connection [" + this.connection + "]");
            }
            this.connection.commit();
        }
    }

    /**
   * {@inheritDoc}
   */
    @Override
    public void rollback() throws SQLException {
        if (this.connection != null && !this.isConnectionTransactional && !this.autoCommit) {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Rolling back JDBC Connection [" + this.connection + "]");
            }
            this.connection.rollback();
        }
    }

    /**
   * {@inheritDoc}
   */
    @Override
    public void close() throws SQLException {
        DataSourceUtils.releaseConnection(this.connection, this.dataSource);
    }

}

TransactionFactory

  • org.apache.ibatis.transaction.TransactionFactory ,Transaction 工厂接口
public interface TransactionFactory {

    // 设置工厂的属性
    default void setProperties(Properties props) {
        // NOP
    }

    // 根据连接创建事务
    Transaction newTransaction(Connection conn);

    // 根据数据源创建事务
    Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);

}
JdbcTransactionFactory
  • org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory ,实现 TransactionFactory 接口,JdbcTransaction 工厂实现类
public class JdbcTransactionFactory implements TransactionFactory {

    @Override
    public Transaction newTransaction(Connection conn) {
        // 创建对象
        return new JdbcTransaction(conn);
    }

    @Override
    public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
        // 创建对象
        return new JdbcTransaction(ds, level, autoCommit);
    }
}
ManagedTransactionFactory
  • org.apache.ibatis.transaction.managed.ManagedTransactionFactory ,实现 TransactionFactory 接口,ManagedTransaction 工厂实现类
public class ManagedTransactionFactory implements TransactionFactory {

    // 是否关闭连接
    private boolean closeConnection = true;

    // 设置属性
    @Override
    public void setProperties(Properties props) {
        if (props != null) {
            String closeConnectionProperty = props.getProperty("closeConnection");
            if (closeConnectionProperty != null) {
                closeConnection = Boolean.parseBoolean(closeConnectionProperty);
            }
        }
    }

    @Override
    public Transaction newTransaction(Connection conn) {
        // 创建对象
        return new ManagedTransaction(conn, closeConnection);
    }

    @Override
    public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
        // 创建对象
        return new ManagedTransaction(ds, level, closeConnection);
    }
}
SpringManagedTransactionFactory
  • org.mybatis.spring.transaction.SpringManagedTransactionFactory ,实现 TransactionFactory 接口,SpringManagedTransaction 工厂实现类。实际真正在使用的
/**
 *    Copyright 2010-2015 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.mybatis.spring.transaction;

import java.sql.Connection;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.ibatis.session.TransactionIsolationLevel;
import org.apache.ibatis.transaction.Transaction;
import org.apache.ibatis.transaction.TransactionFactory;

/**
 * Creates a {@code SpringManagedTransaction}.
 *
 * @author Hunter Presnall
 * 
 * @version $Id$
 */
public class SpringManagedTransactionFactory implements TransactionFactory {

    /**
   * {@inheritDoc}
   */
    @Override
    public Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) {
        return new SpringManagedTransaction(dataSource);
    }

    /**
   * {@inheritDoc}
   */
    @Override
    public Transaction newTransaction(Connection conn) {
        throw new UnsupportedOperationException("New Spring transactions require a DataSource");
    }

    /**
   * {@inheritDoc}
   */
    @Override
    public void setProperties(Properties props) {
        // not needed in this version
    }

}
1
https://gitee.com/shokaku/Java-Review.git
git@gitee.com:shokaku/Java-Review.git
shokaku
Java-Review
Java-Review
master

搜索帮助