搞懂 MyBatis 的事务管理机制

news/2024/11/6 13:30:43/

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

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

  1. 事务概述
  2. MyBatis 实现事务的方式
  3. 事务实现源码分析

一、事务概述

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

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

在MyBatis中,我们可以通过三种方式来管理事务:

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

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

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

以上三种方式,最常用的是声明式管理事务,它可以将事务管理和业务逻辑分离,降低代码的耦合度,提高代码的可读性和可维护性。在MyBatis中,声明式事务管理需要借助Spring框架来实现。

在使用声明式事务管理时,我们需要在Spring配置文件中配置TransactionManager和TransactionProxyFactoryBean两个bean,其中TransactionManager负责事务管理,TransactionProxyFactoryBean负责AOP代理。

下面是一个示例:

 

xml

复制代码

<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,并调用其中的方法:

 

java

复制代码

@Service public class UserService { @Autowired UserMapper userMapperProxy; @Transactional public void addUser(User user) { userMapperProxy.addUser(user); } }

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

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

二、MyBatis 实现事务的方式

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

1. 编程式事务

编程式事务就是通过编写代码来手动管理事务,主要包括以下几个步骤:

1)获取 SqlSession 对象;

2)设置自动提交为 false;

3)执行数据库操作;

4)提交事务;

5)关闭 SqlSession。

代码示例如下:

 

Java

复制代码

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

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

2. 声明式事务

声明式事务就是在配置文件中通过事务管理器来管理事务,这种方式更加灵活和方便,我们只需要在配置文件中配置好即可。MyBatis 内置了两个事务管理器:JDBC 和 Spring。

(1)JDBC 事务管理器

JDBC 事务管理器是 MyBatis 内置的一个事务管理器,在 SqlSessionFactory 的配置文件中进行配置:

 

xml

复制代码

<transactionManager type="JDBC" />

(2)Spring 事务管理器

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

 

xml

复制代码

<transactionManager type="SPRING" />

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

三、事务源码理解

(1)TransactionFactory

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

 

Java

复制代码

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

该方法用于创建一个新的 Transaction 实例。

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

 

Java

复制代码

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 的配置文件中,可以直接引用这个工厂。

例如:

 

xml

复制代码

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

以上代码中,我们使用了 <transactionManager type="JDBC"/> 来配置事务管理器的类型,并且在 <dataSource> 标签中指定了数据源的类型为 POOLED,也就是一个连接池数据源。

在实际应用中,还可以通过自定义事务工厂来创建其他类型的事务。例如,如果你要使用 Atomikos 来管理事务,可以自定义一个 AtomikosTransactionFactory 类来创建事务工厂。

(2)JdbcTransaction

在 MyBatis 中,Transaction 接口的默认实现是 JdbcTransaction 类。这个类的创建是在 JdbcTransactionFactory 工厂类中完成的。

以下是 JdbcTransactionFactory 工厂类中用于创建 Transaction 实例的代码:

 

Java

复制代码

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

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

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

以下是 JdbcTransaction 类的主要源码:

 

Java

复制代码

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 类的主要源码:

 

Java

复制代码

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; } @Override public Connection getConnection() throws SQLException { if (this.connection == null) { openConnection(); } return this.connection; } // ...其他方法 }

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

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

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

四、测试用例

接下来我们将通过一个示例来详细介绍 MyBatis 的事务管理机制。假设我们需要执行一个复杂的业务操作,需要同时更新多个表,我们就可以使用编程式事务来管理事务。

在 Mapper.xml 文件中,编写如下 SQL 语句:

 

xml

复制代码

<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 文件中的方法,代码如下:

 

java

复制代码

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 { // 关闭 SqlSession sqlSession.close(); } } }

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

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


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

相关文章

三十二、自定义镜像

1 、Docker镜像的原理 Docker镜像本质是什么? Docker中一个centos镜像为什么只有200MB&#xff0c;而一个centos操作系统的iso文件要几个G? Docker中一个tomcat镜像为什么有500MB&#xff0c;而一个tomcat安装包只有10多MB? 操作系统组成部分: 计算机组成原理 进程调度子…

Java项目中常用的SON转换方式及示例

摘要: JSON&#xff08;JavaScript Object Notation&#xff09;是一种常用的数据交换格式&#xff0c;用于在不同的应用程序之间传输和存储数据。在Java开发中&#xff0c;我们经常需要将Java对象转换为JSON格式&#xff0c;或者将JSON转换回Java对象。本文将介绍几种常见的JS…

卫龙上市后首份财报:营收净利双降、去年净利下滑8成

当你吃辣条的时候&#xff0c;你在吃什么&#xff1f; 味道&#xff1f;口感&#xff1f;还是童年的记忆&#xff1f; 近日&#xff0c;卫龙美味全球控股有限公司&#xff08;下称“卫龙”&#xff09;发布了上市后的首份年报。 卫龙是一家辣味休闲食品的企业&#xff0c;根…

如何在 Mac 或 Windows 上将 PDF 转换为 Word 而不丢失格式

PDF 有无数的优点&#xff0c;但它不能像 Microsoft Word 文档那样容易编辑。如果您没有价格总是很高的 PDF 编辑器&#xff0c;您将无法根据需要编辑或使用 PDF 源。但是我们可以将PDF转成Word&#xff0c;方便编辑。 有很多解决方案可用于在 Mac 上将 PDF 转换为可编辑的 W…

〖Web全栈开发①〗—网络编程基础(上)

网络编程基础 网络编程网络编程概述TCP/IP协议IP地址什么是IPIP组成IP 地址使用过程查看IPIp地址分类&#xff1a;子网掩码 端口 socketSocket原理&#xff11;.什么是Socket2.创建一个tcp socket&#xff08;tcp套接字&#xff09; tcp 介绍 &#x1f3d8;️&#x1f3d8;️个…

awk命令编辑

awk工作原理 逐行读取文本&#xff0c;默认以空格或tab键分隔符进行分隔&#xff0c;将分隔所得的各个字段保存到内建变量中&#xff0c;并按模式或者条件执行编辑命令。 sed命令常用于一整行的处理&#xff0c;而awk比较倾向于将一行分成多个“字段”然后再进行处理。awk信息…

WINCC和EXCEL的OPC通讯

Option Explicit Option Base 1 Const ServerName “OPCServer.WinCC” Dim WithEvents MyOPCServer As OpcServer Dim WithEvents MyOPCGroup As OPCGroup Dim MyOPCGroupColl As OPCGroups Dim MyOPCItemColl As OPCItems Dim MyOPCItems As OPCItems Dim MyOPCItem As OPCI…

我的服务器被挖矿了,原因竟是。。。

「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;对网络安全感兴趣的小伙伴可以关注专栏《网络安全入门到精通》 挖矿木马应急响应 一、什么是挖矿二、被挖矿主机现象三、挖矿木马处置思路1&#xff09;隔…