《深入理解mybatis原理》 MyBatis事务管理机制

2023-11-11

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010349169/article/details/37992171

   MyBatis作为Java语言的数据库框架,对数据库的事务管理是其非常重要的一个方面。本文将讲述MyBatis的事务管理的实现机制。首先介绍MyBatis的事务Transaction的接口设计以及其不同实现JdbcTransaction 和 ManagedTransaction;接着,从MyBatis的XML配置文件入手,讲解MyBatis事务工厂的创建和维护,进而阐述了MyBatis事务的创建和使用;最后分析JdbcTransactionManagedTransaction的实现和二者的不同特点。

以下是本文的组织结构:

一、概述

    对数据库的事务而言,应该具有以下几点:创建(create)、提交(commit)、回滚(rollback)、关闭(close)。对应地,MyBatis将事务抽象成了Transaction接口:其接口定义如下:

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

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

二、使用MANAGED的事务管理机制:这种机制MyBatis自身不会去实现事务管理,而是让程序的容器如(JBOSS,Weblogic)来实现对事务的管理

这两者的类图如下所示:

二、事务的配置、创建和使用

1. 事务的配置

我们在使用MyBatis时,一般会在MyBatisXML配置文件中定义类似如下的信息:

<environment>节点定义了连接某个数据库的信息,其子节点<transactionManager> 的type 会决定我们用什么类型的事务管理机制。

2.事务工厂的创建

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

    /**
     * 解析<transactionManager>节点,创建对应的TransactionFactory
     * @param context
     * @return
     * @throws Exception
     */
  private TransactionFactory transactionManagerElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type");
      Properties props = context.getChildrenAsProperties();
      /*
            在Configuration初始化的时候,会通过以下语句,给JDBC和MANAGED对应的工厂类
            typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
            typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
            下述的resolveClass(type).newInstance()会创建对应的工厂实例
       */
      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对<transactionManager>节点的解析会生成 TransactionFactory实例;而对<dataSource>解析会生成datasouce实例(关于dataSource的解析和原理,读者可以参照我的另一篇博文:《深入理解mybatis原理》 Mybatis数据源与连接池   
),作为<environment>节点,会根据TransactionFactory和DataSource实例创建一个Environment对象,代码如下所示:

 

  private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
      if (environment == null) {
        environment = context.getStringAttribute("default");
      }
      for (XNode child : context.getChildren()) {
        String id = child.getStringAttribute("id");
        //是和默认的环境相同时,解析之
        if (isSpecifiedEnvironment(id)) {
          //1.解析<transactionManager>节点,决定创建什么类型的TransactionFactory
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
          //2. 创建dataSource
          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
          DataSource dataSource = dsFactory.getDataSource();
          //3. 使用了Environment内置的构造器Builder,传递id 事务工厂TransactionFactory和数据源DataSource
          Environment.Builder environmentBuilder = new Environment.Builder(id)
              .transactionFactory(txFactory)
              .dataSource(dataSource);
          configuration.setEnvironment(environmentBuilder.build());
        }
      }
    }
  }

     Environment表示着一个数据库的连接,生成后的Environment对象会被设置到Configuration实例中,以供后续的使用。

 

    上述一直在讲事务工厂TransactionFactory来创建的Transaction,现在让我们看一下MyBatis中的TransactionFactory的定义吧。

3. 事务工厂TransactionFactory

    事务工厂Transaction定义了创建Transaction的两个方法:一个是通过指定的Connection对象创建Transaction,另外是通过数据源DataSource来创建Transaction。与JDBC 和MANAGED两种Transaction相对应,TransactionFactory有两个对应的实现的子类:如下所示:

4. 事务Transaction的创建

          通过事务工厂TransactionFactory很容易获取到Transaction对象实例。我们以JdbcTransaction为例,看一下JdbcTransactionFactory是怎样生成JdbcTransaction的,代码如下:

 

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

 

    如上说是,JdbcTransactionFactory会创建JDBC类型的Transaction,即JdbcTransaction。类似地,ManagedTransactionFactory也会创建ManagedTransaction。下面我们会分别深入JdbcTranaction 和ManagedTransaction,看它们到底是怎样实现事务管理的。

5. JdbcTransaction

    JdbcTransaction直接使用JDBC的提交和回滚事务管理机制 。它依赖与从dataSource中取得的连接connection 来管理transaction 的作用域,connection对象的获取被延迟到调用getConnection()方法。如果autocommit设置为on,开启状态的话,它会忽略commit和rollback。

    直观地讲,就是JdbcTransaction是使用的java.sql.Connection 上的commit和rollback功能,JdbcTransaction只是相当于对java.sql.Connection事务处理进行了一次包装(wrapper),Transaction的事务管理都是通过java.sql.Connection实现的。JdbcTransaction的代码实现如下:

 

/**
 * @see JdbcTransactionFactory
 */
/**
 * @author Clinton Begin
 */
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;
  }
 
  public Connection getConnection() throws SQLException {
    if (connection == null) {
      openConnection();
    }
    return connection;
  }
 
    /**
     * commit()功能 使用connection的commit()
     * @throws SQLException
     */
  public void commit() throws SQLException {
    if (connection != null && !connection.getAutoCommit()) {
      if (log.isDebugEnabled()) {
        log.debug("Committing JDBC Connection [" + connection + "]");
      }
      connection.commit();
    }
  }
 
    /**
     * rollback()功能 使用connection的rollback()
     * @throws SQLException
     */
  public void rollback() throws SQLException {
    if (connection != null && !connection.getAutoCommit()) {
      if (log.isDebugEnabled()) {
        log.debug("Rolling back JDBC Connection [" + connection + "]");
      }
      connection.rollback();
    }
  }
 
    /**
     * close()功能 使用connection的close()
     * @throws SQLException
     */
  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) {
      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);
  }
 
}
 

6. ManagedTransaction

 

    ManagedTransaction让容器来管理事务Transaction的整个生命周期,意思就是说,使用ManagedTransaction的commit和rollback功能不会对事务有任何的影响,它什么都不会做,它将事务管理的权利移交给了容器来实现。看如下Managed的实现代码大家就会一目了然:

 

 
/**
 * 
 * 让容器管理事务transaction的整个生命周期
 * connection的获取延迟到getConnection()方法的调用
 * 忽略所有的commit和rollback操作
 * 默认情况下,可以关闭一个连接connection,也可以配置它不可以关闭一个连接
 * 让容器来管理transaction的整个生命周期
 * @see ManagedTransactionFactory
 */
/**
 * @author Clinton Begin
 */
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) {
      openConnection();
    }
    return this.connection;
  }
 
  public void commit() throws SQLException {
    // Does nothing
  }
 
  public void rollback() throws SQLException {
    // Does nothing
  }
 
  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());
    }
  }
 
}

注意:如果我们使用MyBatis构建本地程序,即不是WEB程序,若将type设置成"MANAGED",那么,我们执行的任何update操作,即使我们最后执行了commit操作,数据也不会保留,不会对数据库造成任何影响。因为我们将MyBatis配置成了“MANAGED”,即MyBatis自己不管理事务,而我们又是运行的本地程序,没有事务管理功能,所以对数据库的update操作都是无效的。

 

 

以上就是 《深入理解mybatis原理》 MyBatis事务管理机制 的全部内容,如有错误或者不准确的地方,请读者指正,共同进步!

 

 

-----------------------------------------------------------------------------------------------------------------------------------------

本文源自  http://blog.csdn.net/luanlouis/,如需转载,请注明出处,谢谢!

 

 

https://blog.csdn.net/luanlouis/article/details/37992171

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

《深入理解mybatis原理》 MyBatis事务管理机制 的相关文章

  • sendto: Network is unreachable问题的解决

    在用busybox生成根文件系统之后 进入系统ping外网是不能用的 因此需要修改一下几个文件 配置ip为固定 并且开机自动启动网卡 在 etc init d rcS中添加一下内容 网络开机自启动设置 ifconfig eth0 up ud

随机推荐

  • RabbitMQ整合SpringBoor以及RabbitMQ的延迟队列

    延迟队列的概念 前面介绍了死信队列 针对于ttl进入死信队列的情况 假如我们把前面的消费者一关闭 然后对所有的消息都进行设置过期时间 这样是不是就形成了一个延迟队列了 使用场景 比如订单超时关闭 假如我们使用定时任务 假如数据量很大的情况下
  • SpringBoot —— 搭建SpringBoot+Maven项目

    文章目录 前言 一 创建步骤 1 创建SpringBoot项目 选择JDK版本 2 填写包名和项目名 3 创建web项目 4 创建web项目 二 测试 1 配置maven 2 创建测试方法 前言 SpringBoot系列Demo代码 使用i
  • 【复盘与分享】第十一届泰迪杯B题:产品订单的数据分析与需求预测

    文章目录 题目 第一问 第二问 2 1 数据预处理 2 2 数据集分析 2 2 1 训练集 2 2 2 预测集 2 3 特征工程 2 4 模型建立 2 4 1 模型框架和评价指标 2 4 2 模型建立 2 4 3 误差分析和特征筛选 2 4
  • python设置绘图大小_解决Python图形界面中设置尺寸的问题

    Python有自己内置的标准GUI库 Tkinter 只要安装好Python就可以调用 今天学习到了图形界面设计的问题 刚开始就卡住了 为啥呢 就是用geometry size 设置窗口尺寸大小 如800X600 X 从哪里来成了问题 首先
  • 你若有心,我怎会无情

    人和人之间 全靠一颗心 情与情之间 全看一份真 一生很长 什么样的人都会遇到 有的人口蜜腹剑 嘴上跟你说着甜言蜜语 心里却盘算着利用你得到些什么 有的人虚伪自私 表面上跟你掏心掏肺 等你需要帮助的时候却转身离开 所以 看人 不能只看表面 也
  • 复制构造函数(拷贝构造函数)

    也许很多C 的初学者都知道什么是构造函数 但是对复制构造函数 copy constructor 却还很陌生 对于我来说 在写代码的时候能用得上复制构造函数的机会并不多 不过这并不说明复制构造函数没什么用 其实复制构造函数能解决一些我们常常会
  • #1062 - Duplicate entry '0' for key 'PRIMARY'—— mysql的小问题

    问题 1062 重复输入 0 原因 我估计可能是数据表中主键这一栏已经有一个为 0 了 一般出现这种问题是以int类型的字段在输入时没有输如数据 而int类型默认值为 0 而你之前第一条数据已经默认主键如 id为默认的 0 了 于是就报错说
  • java list有序还是无序_java的集合框架

    前言 使用java编程语言的开发人员 在日常开发过程中经常会使用到java的一些集合类 不过这些集合类太多 很多人对它们的特点和使用场景不是特别的了解 通过此文给大家总结一下这方面的知识 方便大家面试或者是初学者理解 Java集合类主要由C
  • 四均线交易系统

    策略说明 基于4均线系统进行判断交易 系统构成 5和20周期均线 3和10周期均线 构成的两组不同周期的均线组合 入场条件 当2组均线均成空头排列时且当前价低于上根BAR最低价入场 出场条件 1 小周期空头均线组合成多头排列 2 两组多头均
  • C++ 获取系统时间(微秒)

    int main 程序开始时间 std chrono time point
  • Python制作一款简单的乒乓球小游戏

    开发工具 Python版本 3 6 4 相关模块 pygame模块 以及一些Python自带的模块 相关文件 关注公众号 Python学习指南 回复 乒乓球 即可获取 环境搭建 pip安装需要的相关模块即可 原理简介 游戏规则 操作 玩家1
  • AWS SAA-C03 #49

    A company stores call transcript files on a monthly basis Users access the files randomly within 1 year of the call but
  • 虚拟机磁盘扩容

    1 前言 现在做开发时 虚拟机的使用很多 经常遇到这样的问题 当初创建虚拟机的时候开辟的磁盘空间比较小 随着虚拟机安装的软件越来越多所占的空间也越来越大 导致虚拟机的磁盘空间越来越少 甚至不够用 此时我们便可以对虚拟机的磁盘大小进行扩容 简
  • 中秋闲鱼卖货,月入过万的新玩法?

    中秋节越来越近了 都开始忙着走亲串友 人情社会关系要多走动 虽然大家都在忙着搞钱 但是逢年过节要停下脚步享受美好生活 月圆中秋思念满满每逢佳节倍思亲 在异国他乡的朋友因疫情不能回家团聚 但现在移动互联网给人们生活带来太多便捷 打电话语音视频
  • 获得用户输入的一个字符串,输出其中字符a的出现次数

    task19 获得用户输入的一个字符串 输出其中字符a的出现次数 name wangzilu date 2020 2 19 task 获得用户输入的一个字符串 输出其中字符a的出现次数 first way x str input pleas
  • shell 重定向到文件

    首先明确基本 gt dev null 输出到空设备 表示丢掉输出信息 2 gt 1 将输出到标准错误的信息输出到标准输出设备 通常是屏幕 有3个默认的i o 0 是标准输入 一般是键盘 1 是标准输出 一般是屏幕了 2 是标准错误 有时候屏
  • 编程笔记:Windows Forms in C#

    1 画线时遇到的奇怪问题 以下摘取部分代码 Graphics g null g CreateGraphics private void Form1 MouseMove object sender MouseEventArgs e 下面三行代
  • 第七章、并发编程实战项目

    一 并发任务执行框架 架构师是什么 在一个软件项目开发过程中 将客户的需求转换为规范的开发计划及文本 并制定这个项目的总体架构 指导整个开发团队完成这个计划的那个人 就是 架构师 一般是一个项目里的最资深的专业技术人员 可以说架构师首先一定
  • 【SDOI2016】数字配对【建立二分图+费用流求方案数】

    题目链接 首先 我们可以看一下这个推导过程 如果 那么 对于 就一定不是质数 一定是它的一个因子 于是可以看出 这一定是一幅二分图 于是 可以根据二分图的性质来确定了每个点的属于S边还是T边了 include
  • 《深入理解mybatis原理》 MyBatis事务管理机制

    版权声明 本文为博主原创文章 未经博主允许不得转载 https blog csdn net u010349169 article details 37992171 MyBatis作为Java语言的数据库框架 对数据库的事务管理是其非常重要的