关于mybatis-plus:从零开始手写-mybatis四-mybatis-事务管理机制详解

31次阅读

共计 9177 个字符,预计需要花费 23 分钟才能阅读完成。

前景回顾

第一节 从零开始手写 mybatis(一)MVP 版本 中咱们实现了一个最根本的能够运行的 mybatis。

第二节 从零开始手写 mybatis(二)mybatis interceptor 插件机制详解

第三节 从零开始手写 mybatis(三)jdbc pool 从零实现数据库连接池

本节咱们一起来学习一下 mybatis 中的事务管理。

mybatis 中的事务管理

mybatis 事务有两种应用形式:

  1. 应用 JDBC 的事务管理机制:即应用 java.Sql.Connection 对象实现对事务的提交,回滚和敞开操作。
  2. 应用 MANAGED 的事务管理机制:mybatis 自身不会去实现事务管理的相干操作,而是交个内部容器来治理事务。当与 spring 整合应用后,个别应用 spring 来治理事务。

事务工厂 TransactionFactory

接口定义

这个是对事务的一个工厂,接口如下:

public interface TransactionFactory {

  /**
   * Sets transaction factory custom properties.
   * @param props
   */
  void setProperties(Properties props);

  /**
   * Creates a {@link Transaction} out of an existing connection.
   * @param conn Existing database connection
   * @return Transaction
   * @since 3.1.0
   */
  Transaction newTransaction(Connection conn);
  
  /**
   * Creates a {@link Transaction} out of a datasource.
   * @param dataSource DataSource to take the connection from
   * @param level Desired isolation level
   * @param autoCommit Desired autocommit
   * @return Transaction
   * @since 3.1.0
   */
  Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);

}

次要就是如何依据一个 DataSource 创立一个 Transaction。

实际上整体感觉意义不大。

最外围的还是要看一下 Transaction 的实现。

Transaction 接口

public interface Transaction {

  /**
   * Retrieve inner database connection
   * @return DataBase connection
   * @throws SQLException
   */
  Connection getConnection() throws SQLException;

  /**
   * Commit inner database connection.
   * @throws SQLException
   */
  void commit() throws SQLException;

  /**
   * Rollback inner database connection.
   * @throws SQLException
   */
  void rollback() throws SQLException;

  /**
   * Close inner database connection.
   * @throws SQLException
   */
  void close() throws SQLException;

  /**
   * Get transaction timeout if set
   * @throws SQLException
   */
  Integer getTimeout() throws SQLException;}

这里最外围的实际上只有 commit() 和 rollback(),其余的都是能够疏忽的。

针对 getTimeout() 咱们就能够为 mybatis 提供一个操作的超时机制。

JdbcTransaction 实现

基于 jdbc 机制的一些解决。

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

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

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

这里整体的实现实际上非常简单,就是被动设置了一下主动提交的属性。

ManagedDataSource

这个是另一个实现,实际上更加简略。

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

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

}

作用

ManagedTransaction 对事务的 commit 和 rollback 交给了容器去治理,本人自身并没有做任何解决。

mybatis 的应用形式

如果 Mybatis 是独自运行的,没有其余框架治理,此时 mybatis 外部会对下段代码实现。

con.setAutoCommit(false);
// 此处命令告诉数据库, 从此刻开始从以后 Connection 通道推送而来的
//SQL 语句属于同一个业务中这些 SQL 语句在数据库中应该保留到同一个
//Transaction 中. 这个 Transaction 的行为 (commit,rollback) 由以后 Connection 治理.
try{
    // 推送 sql 语句命令……..;
    con.commit();// 告诉 Transaction 提交.}catch(SQLException ex){con.rollback();// 告诉 Transaction 回滚.
}

整体来说这种写法比拟原始,咱们能够将原本交给 connection 解决的事务,对立调整为应用事务管理器解决。

spring 整合

当然针对 mybatis,大部分都是单个语句的执行。

用于应用 connection 时,实际上失去的是 mybatis 事务管理器封装之后的 connection。

实际上 spring 的整合,可能适用性更强一些。

集体实现

看完了 mybatis 的实现原理之后,咱们的实现就变得非常简单。

咱们能够简化下面的一些实现,保留外围的局部即可。

接口定义

咱们只保留外围的 3 个接口。

/**
 * 事务管理
 */
public interface Transaction {

    /**
     * Retrieve inner database connection
     * @return DataBase connection
     */
    Connection getConnection();

    /**
     * Commit inner database connection.
     */
    void commit();

    /**
     * Rollback inner database connection.
     */
    void rollback();}

ManageTransaction

这个实现,咱们的 commit 和 rollback 什么都不做。

/**
 * 事务管理
 *
 * @since 0.0.18
 */
public class ManageTransaction implements Transaction {

    /**
     * 数据信息
     * @since 0.0.18
     */
    private final DataSource dataSource;

    /**
     * 隔离级别
     * @since 0.0.18
     */
    private final TransactionIsolationLevel isolationLevel;

    /**
     * 连贯信息
     * @since 0.0.18
     */
    private Connection connection;

    public ManageTransaction(DataSource dataSource, TransactionIsolationLevel isolationLevel) {
        this.dataSource = dataSource;
        this.isolationLevel = isolationLevel;
    }

    public ManageTransaction(DataSource dataSource) {this(dataSource, TransactionIsolationLevel.READ_COMMITTED);
    }

    @Override
    public Connection getConnection() {
        try {if(this.connection == null) {Connection connection = dataSource.getConnection();
                connection.setTransactionIsolation(isolationLevel.getLevel());
                this.connection = connection;
            }

            return connection;
        } catch (SQLException throwables) {throw new MybatisException(throwables);
        }
    }

    @Override
    public void commit() {//nothing}

    @Override
    public void rollback() {//nothing}

}

JdbcTransaction.java

这里和下面的相比拟,多出了 commit 和 rollback 的逻辑解决。

package com.github.houbb.mybatis.transaction.impl;

import com.github.houbb.mybatis.constant.enums.TransactionIsolationLevel;
import com.github.houbb.mybatis.exception.MybatisException;
import com.github.houbb.mybatis.transaction.Transaction;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * 事务管理
 *
 * @since 0.0.18
 */
public class JdbcTransaction implements Transaction {

    /**
     * 数据信息
     * @since 0.0.18
     */
    private final DataSource dataSource;

    /**
     * 隔离级别
     * @since 0.0.18
     */
    private final TransactionIsolationLevel isolationLevel;

    /**
     * 主动提交
     * @since 0.0.18
     */
    private final boolean autoCommit;

    /**
     * 连贯信息
     * @since 0.0.18
     */
    private Connection connection;

    public JdbcTransaction(DataSource dataSource, TransactionIsolationLevel isolationLevel, boolean autoCommit) {
        this.dataSource = dataSource;
        this.isolationLevel = isolationLevel;
        this.autoCommit = autoCommit;
    }

    public JdbcTransaction(DataSource dataSource) {this(dataSource, TransactionIsolationLevel.READ_COMMITTED, true);
    }

    @Override
    public Connection getConnection(){
        try {if(this.connection == null) {Connection connection = dataSource.getConnection();
                connection.setTransactionIsolation(isolationLevel.getLevel());
                connection.setAutoCommit(autoCommit);
                this.connection = connection;
            }

            return connection;
        } catch (SQLException throwables) {throw new MybatisException(throwables);
        }
    }

    @Override
    public void commit() {
        try {
            // 非主动提交,才执行 commit 操作
            if(connection != null && !this.autoCommit) {connection.commit();
            }
        } catch (SQLException throwables) {throw new MybatisException(throwables);
        }
    }

    @Override
    public void rollback() {
        try {
            // 非主动提交,才执行 commit 操作
            if(connection != null && !this.autoCommit) {connection.rollback();
            }
        } catch (SQLException throwables) {throw new MybatisException(throwables);
        }
    }

}

正文完
 0