前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >MyBatis源码解析之基础模块—Transaction

MyBatis源码解析之基础模块—Transaction

原创
作者头像
todobugs
修改于 2020-10-23 09:55:29
修改于 2020-10-23 09:55:29
62700
代码可运行
举报
文章被收录于专栏:mybatis源码分析mybatis源码分析
运行总次数:0
代码可运行

MyBatis源码解析之基础模块—Transaction

前文回顾

上一篇,咱们一起学习了Mybatis的DataSource模块相关源码,掌握了三种数据源工厂的逻辑,同时也掌握非池化数据源的连接创建,池化数据源如何从空闲列表获取连接并放到活跃连接列表,及连接的归还到空闲队列的逻辑。

下面跟随笔者的思路,咱们继续学习另一个重要模块——Transaction模块。

核心要点

本篇幅主要讲解Mybatis在事务管理的抽象方案,以及提供的两种简单实现:jdbc实现及外部容器的处理逻辑。

架构设计

Transaction模块所在包路径为org.apache.ibatis.transaction,其具体划分如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
transaction
- jdbc
  - JdbcTransaction
  - JdbcTransactionFactory
- managed
  - ManagedTransaction
  - ManagedTransactionFactory
- DataSourceException
- TransactionFactory
- Transaction:

老规矩,咱们先从宏观架构图上了解设计脉络,见下图:

不想说又不得不说,Mybatis真是将Factory设计模式用户的炉火纯青。纵观整个项目源码,大量的使用工厂或工厂方法模式。

源码解读

基于面向接口编程的思路,咱们首先看下Mybatis事务管理中的两个核心接口:TransactionTransactionFactory

Transaction

正如Mybatis作者对该接口的定义:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// Handles the connection lifecycle that comprises: its creation, preparation, commit/rollback and close.

翻译过来就是:处理整个连接生命周期的各种操作,包括连接创建,事务提交、回滚及连接关闭等数据库事务相关的操作。

该接口共有五个方法。对于码畜们来说,源码加注释才是国际通用语言。各方法描述如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 处理整个连接生命周期的各种操作,包括连接创建,事务提交、回滚及连接关闭等数据库事务相关的操作。
 */
public interface Transaction {/** 获取数据库连接 */
  Connection getConnection() throws SQLException;/** 数据提交 */
  void commit() throws SQLException;/** 数据回滚 */
  void rollback() throws SQLException;/** 关闭数据库连接 */
  void close() throws SQLException;/** 获取超时时间,如果设置的话,Mybatis默认的两种实现,均设置为null */
  Integer getTimeout() throws SQLException;
}

以上为Transaction事务处理的核心接口方法。具体的实现逻辑等咱们稍后进行解析说明。

TransactionFactory

见名知意,该接口为事务的创建工厂,其唯一的目的就是创建事务对象。从源码中可以看出TransactionFactory功能非常简单,就是获取事务对象。当然获取事务前会设置相关属性。具体见代码加注释:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package org.apache.ibatis.transaction;import java.sql.Connection;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.ibatis.session.TransactionIsolationLevel;/**
 * 事务创建工厂接口
 */
public interface TransactionFactory {
   */
  /** 设置相关配置信息 */
  default void setProperties(Properties props) {
    // NOP
  }/** 根据连接对象获取事务对象 */
  Transaction newTransaction(Connection conn);/**根据数据源、事务隔离级别、是否自动提交属性 构建事务对象*/
  Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
}

一个设置配置信息的方法,两个获取事务对象的重载方法。

JdbcTransaction

接下来看下基于jdbc的事务管理方案:

JdbcTransaction类实现中有四个属性。分别为连接对象、数据源、事务隔离级别、是否自动提交等。

其构造方法有两个。一个是基于connection对象为参数的构造方法。一个则是基于数据源、事务隔离级别、autoCommit为参数的构造方法。

然后学习下JdbcTransaction实现的几个方法:

getConnection() :首选判断connection对象是否为null,为null的情况下通过调用私有方法openConnection()来获取连接对象。

commit():在connection非空且设置为手动提交的方式下,执行数据库连接提交操作。

rollback():在connection非空且设置为手动提交的方式下,执行数据库连接回滚操作。

close():在connection非空时执行数据库连接关闭操作,当然,如果数据库为手动提交,则先设置为自动提交。

具体请看源码及注释:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package org.apache.ibatis.transaction.jdbc;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.session.TransactionIsolationLevel;
import org.apache.ibatis.transaction.Transaction;
import org.apache.ibatis.transaction.TransactionException;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 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();
    }
  }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);
      }
    }
  }/**通过datasource获取数据库连接,然后属性level、autoCommit设置对应的事务隔离级别和是否自动提交*/
  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);
  }
  
  /** 设置是否自动提交 */
  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);
    }
  }
​
  @Override
  public Integer getTimeout() throws SQLException {
    return null;
  }
}

JdbcTransactionFactory

JdbcTransactionFactory非常简单,只是实现了TransactionFactory两个获取实例的方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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);
  }
}
ManagedTransaction

通过其名字可知,该事务管理方式是有外部容器托管的。ManagedTransaction 与JdbcTransaction一样也有四个属性,不过区别的一个属性是boolean类型的closeConnection属性,与之对应的构造方法也有所区别。

对应ManagedTransaction的接口实现,其数据库连接对象获取也是通过datasource,而commit、rollback则是空实现,相对应的提交回滚则由外部容器来完成。

对于事务的关闭,则根据属性字段closeConnection值来决定。具体请查看代码及注释:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 {
    // closeConnection 为true 且connection不为空时执行关闭
    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关于事务的实现比较简单。抽象了事务管理对象并提供了一个简单的jdbc类型实现。而Managed基本是空实现,最终会有外部容器进行托管。具体会在MyBatis集成Spring中进行说明。

关于MyBatis的Transaction模块介绍至此告一段落。感谢垂阅,如有不妥之处请多多指教~


微观世界,达观人生。

做一名踏实的coder !

欢迎关注我的个人微信公众号 : todobugs

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
《深入理解mybatis原理》 MyBatis事务管理机制
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://louluan.blog.csdn.net/article/details/37992171
亦山
2019/05/25
6240
mybatis源码解读(四)——事务的配置
  上一篇博客我们介绍了mybatis中关于数据源的配置原理,本篇博客介绍mybatis的事务管理。   对于事务,我们是在mybatis-configuration.xml 文件中配置的:   关于
IT可乐
2018/05/28
7090
MyBatis源码阅读(十一) --- MyBatis事务管理机制
事务的概念,大家都不会陌生。在我们写增删改的时候,我们肯定都需要加上事务,来保证数据的一致性。MyBatis作为Java语言的数据库框架,对数据库的事务管理是其非常重要的一个方面。在Mybatis中,同样提供了事务的功能,所以我们有必要了解一下MyBatis的事务管理的实现机制。
终有救赎
2024/01/10
2160
MyBatis源码阅读(十一) --- MyBatis事务管理机制
MyBatis 源代码阅读笔记 2 基于"注解"方式的代码编写
https://github.com/Jason-Chen-2017/source-code-reading
一个会写诗的程序员
2019/07/25
5630
Mybatis源码简易导读
TransactionFactory 事务工厂接口,源码如下,主要的作用构造事务的配置信息
用户2603479
2019/08/20
4890
4. 数据源模块
在日常开发中,我们经常会接触到池化技术,这是一种非常经典的设计思想。简单来说,池化技术指的是:将一些创建过程较为繁琐的重量级对象,统一维护在一个对象池中进行管理,每次使用对象时都从池中获取,使用完成后再归还给对象池进行回收。
张申傲
2023/10/12
2550
4. 数据源模块
mybatis源码解读(三)——数据源的配置
  在mybatis-configuration.xml 文件中,我们进行了如下的配置: <!-- 可以配置多个运行环境,但是每个 SqlSessionFactory 实例只能选择一个运行环境常用: 一、development:开发模式 二、work:工作模式 --> <environments default="development"> <!--id属性必须和上面的default一样 --> <environment id="development">
IT可乐
2018/05/28
7150
MyBatis事务管理机制
MyBatis作为Java语言的数据库框架,对数据库的事务管理是其非常重要的一个方面。本文将从事务的分类、配置和实现分析MyBatis的事务管理的实现机制。
java架构师
2019/04/01
8000
MyBatis事务管理机制
MyBatis设计思想(3)——数据源模块
**工厂方法:定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中所要求的“创建与使用相分离”的特点。**通过工厂方法,客户端可以不关心产品的具体创建过程,直接从工厂中获取实例即可。
张申傲
2020/09/03
4190
MyBatis设计思想(3)——数据源模块
MyBatis常用对象SqlSessionFactory和SqlSession介绍和运用
学习框架一个比较好的路径阅读源码.本文介绍的SqlSessionFactory和SqlSession.可以通过了解SqlSessionFactory接口和SqlSession接口以及两个的实现类入手,去看源码了解实现过程.最好能把项目下载到本地,慢慢分析实现过程.
全栈程序员站长
2022/08/11
3180
MyBatis常用对象SqlSessionFactory和SqlSession介绍和运用
mybatis:Creating a new SqlSession Closing non transactional SqlSession[通俗易懂]
在springboot配置了mybatis、Druid数据库连接池后,发现每次sql执行mybatis都:
全栈程序员站长
2022/07/04
3.4K0
myBatis源码学习之SqlSessionFactory
上一篇博客 SqlSessionFactoryBuilder 中介绍了它的作用就是获得DefaultSqlSessionFactory
全栈程序员站长
2022/08/26
4500
Mybatis:颠覆你心中对事务的理解
1.说到数据库事务,人们脑海里自然不自然的就会浮现出事务的四大特性、四大隔离级别、七大传播特性。四大特性还好说,问题是七大传播特性是哪儿来的?是Spring在当前线程内,处理多个数据库操作方法事务时所做的一种事务应用策略。事务本身并不存在什么传播特性,不要混淆事务本身和Spring的事务应用策略。(当然,找工作面试时,还是可以巧妙的描述传播特性的)
后端码匠
2020/01/02
5810
源码中的设计并没有你想象的那么复杂,不信你来看看MyBatis的DataSource的实现
  本文我们来给大家介绍了MyBatis中的DataSource的设计实现。其实蛮容易的哦。
用户4919348
2021/06/01
3210
源码中的设计并没有你想象的那么复杂,不信你来看看MyBatis的DataSource的实现
Mybatis数据源结构解析
对于 ORM 框架而言,数据源的组织是一个非常重要的一部分,这直接影响到框架的性能问题。本文将通过对 MyBatis 框架的数据源结构进行详尽的分析,找出什么时候创建 Connection ,并且深入解析 MyBatis 的连接池。
Java宝典
2020/12/04
5780
mybatis 核心原理 线程隔离
建议同时学习@Transaction, spring对事务的管理 spring @Transactional原理
平凡的学生族
2020/01/02
9160
MyBatis源码解析之基础模块—DataSource
因为常见的数据源都会基于javax.sql.Datasource实现。Mybatis的数据源实现也是基于实现javax.sql.Datasource来设计的,也是在介绍MyBatis数据源实现之前,咱们先了解下JDK的DataSource。
todobugs
2020/10/22
9150
Mybatis 源码分析
框架是对原来操作的扩展封装,接下来我们来看下 Mybatis 是如何将几行代码扩展成一个框架的。
啵啵肠
2023/11/20
1780
mybatis源码(1) -- 如何在Spring中驰骋的
mybatis作为持久层流行框架已经被很多产品使用,当然为了接入Spring这个业内的另一个流行框架,mybatis还是做了些事,通过分析除了明白支持Spring的机制原理还了解Spring对持久层接入留了那些口。 使用 <!-- 配置SqlSessionFactoryBean --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name
alexqdjay
2018/05/11
9710
Mybatis源码解析(五):SqlSession会话的创建
冬天vs不冷
2025/01/21
1440
Mybatis源码解析(五):SqlSession会话的创建
相关推荐
《深入理解mybatis原理》 MyBatis事务管理机制
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验