【MyBatis】搞懂 MyBatis 的事务管理机制

news/2024/11/8 4:33:45/

文章目录

  • 前言
  • 一、事务概述
  • 二、MyBatis 实现事务的方式
    • 1. 编程式事务
    • 2. 声明式事务
  • 三、事务源码理解
    • (1)TransactionFactory
    • (2)JdbcTransaction
    • (3)ManagedTransaction
  • 四、测试用例
  • 总结

前言

MyBatis 是一款优秀的持久层框架,相信很多 Java 后端开发人员对它都不会陌生。在实际项目开发中,事务管理是非常重要的一环,而 MyBatis 也为我们提供了便捷的事务管理机制。

本文将从以下方面详细解析 MyBatis 的事务管理机制:

  • 事务概述
  • MyBatis 实现事务的方式
  • 事务实现源码分析

一、事务概述

事务是指要么全部执行成功,要么全部回滚的一组操作。在数据库中,一般使用 ACID
规则来约束事务,即原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。在
MyBatis 中,也遵循了这一规则。

MyBatis中的事务是指一系列数据库操作,这些操作要么全部执行成功,要么全部执行失败。如果操作过程中发生错误,所有对数据库的修改都将被回滚,即还原到最初状态。事务是确保数据一致性和完整性的关键机制之一。
在MyBatis中,我们可以通过三种方式来管理事务:

  1. 编程式管理事务:在代码中显式开启、提交或回滚事务。

  2. 声明式管理事务:通过AOP代理实现事务管理,可以让代码更简洁,更容易维护。

  3. 注解式管理事务:通过注解方式管理事务,是声明式管理事务的一种扩展方式。

以上三种方式,最常用的是声明式管理事务,它可以将事务管理和业务逻辑分离,降低代码的耦合度,提高代码的可读性和可维护性。在MyBatis中,声明式事务管理需要借助Spring框架来实现。
在使用声明式事务管理时,我们需要在Spring配置文件中配置TransactionManager和TransactionProxyFactoryBean两个bean,其中TransactionManager负责事务管理,TransactionProxyFactoryBean负责AOP代理。
下面是一个示例:

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource" />
</bean><bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"><property name="mapperInterface" value="com.example.UserMapper"/><property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean><bean id="userMapperProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"><property name="transactionManager" ref="transactionManager" /><property name="target" ref="userMapper" /><property name="transactionAttributes"><props><prop key="select*">PROPAGATION_REQUIRED,readOnly</prop><prop key="insert*">PROPAGATION_REQUIRED</prop><prop key="update*">PROPAGATION_REQUIRED</prop><prop key="delete*">PROPAGATION_REQUIRED</prop></props></property>
</bean>

在编写完上述配置后,我们就可以对UserMapper接口中的方法进行事务管理了。例如,在UserService类中,我们可以注入userMapperProxy,并调用其中的方法:

@Service
public class UserService {@AutowiredUserMapper userMapperProxy;@Transactionalpublic void addUser(User user) {userMapperProxy.addUser(user);}
}

在代码中,通过@Transactional注解声明了该方法需要进行事务管理,当方法执行过程中发生异常时,事务将会回滚。

MyBatis支持多种事务管理方式,但是建议使用声明式事务管理,因为它能够最大程度地降低代码的耦合度,并提高代码的可读性和可维护性。

二、MyBatis 实现事务的方式

MyBatis 实现事务也有多种方式,其中最常用的方式有两种:编程式事务和声明式事务。接下来我们将分别来介绍这两种方式。

1. 编程式事务

编程式事务就是通过编写代码来手动管理事务,主要包括以下几个步骤:
1)获取 SqlSession 对象;
2)设置自动提交为 false;
3)执行数据库操作;
4)提交事务;
5)关闭 SqlSession。
代码示例如下:

SqlSession sqlSession = sqlSessionFactory.openSession(false);
try {// 执行数据库操作sqlSession.insert("insertUser", user);sqlSession.update("updateUser", user);// 提交事务sqlSession.commit();
} catch (Exception e) {// 回滚事务sqlSession.rollback();
} finally {// 关闭 SqlSessionsqlSession.close();
}

以上代码中,通过设置自动提交为 false 来关闭自动提交事务的方式,然后在 try 块中执行需要进行事务管理的数据库操作,如果出现异常则回滚事务,否则提交事务,并在 finally 中关闭 SqlSession。

2. 声明式事务

声明式事务就是在配置文件中通过事务管理器来管理事务,这种方式更加灵活和方便,我们只需要在配置文件中配置好即可。MyBatis 内置了两个事务管理器:JDBC 和 Spring。
(1)JDBC 事务管理器
JDBC 事务管理器是 MyBatis 内置的一个事务管理器,在 SqlSessionFactory 的配置文件中进行配置:
<transactionManager type="JDBC" />

(2)Spring 事务管理器
Spring 事务管理器是基于 Spring 框架的事务管理器,需要引入 Spring 的包,并在 MyBatis 的配置文件中配置:

<transactionManager type="SPRING" />

在使用 Spring 事务管理器时,还需要在 Spring 的配置文件中配置事务管理器和数据源,具体可以参考 Spring 的文档。

三、事务源码理解

(1)TransactionFactory

在 MyBatis 中,事务工厂需要实现 org.apache.ibatis.transaction.TransactionFactory 接口,该接口有一个方法:

Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);

该方法用于创建一个新的 Transaction 实例。
例如,如果你要使用 JDBC 来管理事务,可以使用默认提供的 JdbcTransactionFactory 类来创建事务工厂。以下是一个创建事务工厂的示例代码:

import org.apache.ibatis.transaction.Transaction;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;import javax.sql.DataSource;public class MyTransactionFactory {public static TransactionFactory createTransactionFactory() {return new JdbcTransactionFactory();}
}

以上代码中,我们使用 JdbcTransactionFactory 创建一个新的事务工厂。实际上,在 MyBatis 的配置文件中,可以直接引用这个工厂。
例如:

<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><!-- Mapper 配置 -->
</configuration>

以上代码中,我们使用了 来配置事务管理器的类型,并且在 标签中指定了数据源的类型为 POOLED,也就是一个连接池数据源。
在实际应用中,还可以通过自定义事务工厂来创建其他类型的事务。例如,如果你要使用 Atomikos 来管理事务,可以自定义一个 AtomikosTransactionFactory 类来创建事务工厂。

(2)JdbcTransaction

在 MyBatis 中,Transaction 接口的默认实现是 JdbcTransaction 类。这个类的创建是在 JdbcTransactionFactory 工厂类中完成的。
以下是 JdbcTransactionFactory 工厂类中用于创建 Transaction 实例的代码:

@Override
public Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) {return new JdbcTransaction(dataSource, level, autoCommit);
}

在这段代码中,我们可以看到工厂方法 newTransaction() 的实现。该方法接收三个参数:数据源、事务隔离级别和是否自动提交。

在 newTransaction 方法中,我们使用这些参数创建一个新的 JdbcTransaction 实例,并将其返回。JdbcTransaction 类中实现了 Transaction 接口,因此可以直接返回。

以下是 JdbcTransaction 类的主要源码:

public class JdbcTransaction implements Transaction {protected Connection connection;protected DataSource dataSource;protected TransactionIsolationLevel level;protected boolean autoCommit;public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) throws SQLException {dataSource = ds;level = desiredLevel;autoCommit = desiredAutoCommit;openConnection();setDesiredAutoCommit();setDesiredTransactionIsolation();}// ...其他方法}

在 JdbcTransaction 类中,我们创建了一个基本的事务对象,它包含了一些属性,例如事务管理器、连接、事务隔离级别等等。在构造函数中,我们使用传入的参数来初始化这些属性值

除了构造函数之外,还有一些其他的方法用于对事务进行操作,例如提交、回滚、关闭等。这些方法都是从 Transaction 接口中继承来的。

因此,在 MyBatis 中,创建一个新的事务就是通过事务工厂和相应的创建方法来创建一个特定类型的事务对象。而在事务对象中,我们可以对事务进行各种操作。

(3)ManagedTransaction

在 MyBatis 中,除了 JdbcTransaction 之外,还提供了另外一种称为 ManagedTransaction 的事务实现,它是一种由容器或框架来管理的事务,而不是 MyBatis 内部来管理。
ManagedTransaction 接口的默认实现是 SpringManagedTransaction 类。该类实现了 MyBatis 的 Transaction 接口,但其本身并不处理任何事务逻辑,而是委托给 Spring 框架进行处理。
以下是 SpringManagedTransaction 类的主要源码:

public class SpringManagedTransaction implements Transaction {private final DataSource dataSource;private Connection connection;private boolean isConnectionTransactional;private boolean autoCommit;public SpringManagedTransaction(DataSource dataSource) {this.dataSource = dataSource;}@Overridepublic Connection getConnection() throws SQLException {if (this.connection == null) {openConnection();}return this.connection;}// ...其他方法}

在 SpringManagedTransaction 类中,我们使用传入的数据源来创建一个新的事务对象。在 getConnection() 方法中,如果当前连接不存在,则会打开一个新的连接并返回;否则直接返回已经存在的连接。

需要注意的是,SpringManagedTransaction 并不自行管理事务,而是将事务管理工作交给了 Spring 框架。因此,如果要使用 SpringManagedTransaction,我们需要在 Spring 配置文件中配置相应的事务管理器,例如 DataSourceTransactionManager 或 JpaTransactionManager 等。

总之,相比于 JdbcTransaction,ManagedTransaction 更适合在容器或框架中使用,而不是在 MyBatis 内部使用。如果你使用 Spring 或其他类似的框架来管理事务,则可以考虑使用 ManagedTransaction 来实现 MyBatis 的事务管理。

四、测试用例

接下来我们将通过一个示例来详细介绍 MyBatis 的事务管理机制。假设我们需要执行一个复杂的业务操作,需要同时更新多个表,我们就可以使用编程式事务来管理事务。
在 Mapper.xml 文件中,编写如下 SQL 语句:

<insert id="insertUser" parameterType="com.example.User">insert into user (id, name, age) values (#{id}, #{name}, #{age})
</insert><update id="updateAccount" parameterType="com.example.Account">update account set balance = #{balance} where id = #{id}
</update>

然后我们编写一个 Service 类来调用 Mapper 文件中的方法,代码如下:

public class UserService {private SqlSessionFactory sqlSessionFactory;public UserService(SqlSessionFactory sqlSessionFactory) {this.sqlSessionFactory = sqlSessionFactory;}public void addUserAndAccount(User user, Account account) {SqlSession sqlSession = sqlSessionFactory.openSession(false);try {// 执行数据库操作sqlSession.insert("insertUser", user);sqlSession.update("updateAccount", account);// 提交事务sqlSession.commit();} catch (Exception e) {// 回滚事务sqlSession.rollback();} finally {// 关闭 SqlSessionsqlSession.close();}}
}

以上代码中,我们通过 openSession(false) 来获取 SqlSession 对象,并设置自动提交为 false,然后在 try 块中执行数据库操作,如果出现异常则回滚事务,否则提交事务,并在 finally 中关闭 SqlSession。在实际开发中,我们可以将 SqlSession 放入 Spring 管理中,这样就更加方便了。

总结

以上就是 MyBatis 的事务管理机制的详细介绍和代码示例,希望能对大家有所帮助。


http://www.ppmy.cn/news/67947.html

相关文章

jvm-狂神课程

一、JVM JVM就是Java虚拟机&#xff0c;Java虚拟机就是JVM 1. JVM位置 1、Java程序&#xff08;跑的环境是在jvm&#xff08;虚拟机&#xff09;跑的&#xff0c;也可以说是在jre上跑的&#xff09;java运行是需要在特定的环境的也就是这个jre这种。 2、jvm&#xff08;也就是…

【C++】类和对象(中)---拷贝构造函数、赋值运算符重载

个人主页&#xff1a;平行线也会相交&#x1f4aa; 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 平行线也会相交 原创 收录于专栏【C之路】&#x1f48c; 本专栏旨在记录C的学习路线&#xff0c;望对大家有所帮助&#x1f647;‍ 希望我们一起努力、成长&…

路径规划算法:基于正余弦算法的路径规划算法- 附代码

路径规划算法&#xff1a;基于正余弦优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于正余弦优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能优化算法…

元宇宙虚拟展馆和VR的关系与区别

元宇宙是指一个数字化的虚拟世界&#xff0c;这个世界由虚拟现实、增强现实等多种技术构建而成。这个世界中&#xff0c;人们可以用虚拟身份在其中自由穿梭&#xff0c;享受到与现实世界不同的多样化和极致体验。虚拟现实&#xff08;VR&#xff09;则是一种技术手段&#xff0…

网络编程:TCP socket

文章目录 阅读前导 服务端定义日志框架成员属性服务端框架 初始化服务器创建套接字绑定开启监听 运行服务器netstat 工具获取连接和通信准备通信逻辑 单进程服务端函数&#xff08;version1&#xff09;telent 工具测试 多进程服务端&#xff08;version2&#xff09;创建子进程…

linux中shell脚本的执行过程

Shell脚本是一种在Linux和Unix操作系统中广泛使用的脚本语言&#xff0c;用于自动化和简化各种任务。Shell脚本通常以.sh扩展名保存&#xff0c;并且可以使用文本编辑器创建和修改。在本文中&#xff0c;我们将详细介绍Shell脚本的执行过程&#xff0c;包括Shell解释器的作用&a…

Flink从入门到精通之-10容错机制

Flink从入门到精通之-10容错机制 流式数据连续不断地到来&#xff0c;无休无止&#xff1b;所以流处理程序也是持续运行的&#xff0c;并没有一个明确的结束退出时间。机器运行程序&#xff0c;996 起来当然比人要容易得多&#xff0c;不过希望“永远运行”也是不切实际的。因…

Debian 12 “Bookworm” 的新特性和发布日期

导读Debian 12 即将发布。了解一下更多关于其新特性和发布日期的相关信息。 debian 12 Debian 即将发布系统代号为 “书虫” 的新版本。与 Debian 11 “Bullseye” 相比&#xff0c;有许多改进和新功能。 Debian 12 “Bookworm” 包含了超过 11200 个新软件包&#xff0c;软件…