深入理解 Spring 事务
一、引言
在企业级应用开发中,事务管理是确保数据一致性和完整性的关键环节。Spring 框架提供了强大而灵活的事务管理功能,使得开发者能够方便地在各种应用场景中处理事务。本文将深入探讨 Spring 事务的原理、特性、使用方式,并通过详细的示例代码帮助读者更好地理解和应用 Spring 事务。
二、事务的基本概念
事务是一组逻辑操作单元,这些操作要么全部成功执行,要么全部失败回滚,以保证数据的一致性。例如,在一个银行转账系统中,从一个账户扣款并向另一个账户收款的操作必须作为一个事务来处理。如果扣款成功但收款失败,那么整个事务应该回滚,以确保两个账户的余额数据始终保持正确的状态。
三、Spring 事务的特性
(一)原子性(Atomicity)
原子性确保事务中的所有操作要么全部完成,要么全部不完成。就像一个不可分割的原子,事务中的任何一个操作失败,都会导致整个事务回滚到初始状态,就好像该事务从未发生过一样。
(二)一致性(Consistency)
事务在开始和结束时,数据都必须处于一致的状态。例如,在数据库事务中,遵循特定的约束规则(如主键唯一性、外键约束等),事务执行前后数据都应符合这些规则。
(三)隔离性(Isolation)
多个事务并发执行时,每个事务都应该感觉不到其他事务的存在,它们之间相互隔离。不同的隔离级别可以控制事务之间的可见性和相互影响程度,例如,一个事务不能读取到其他事务未提交的数据(未提交读隔离级别除外)。
(四)持久性(Durability)
一旦事务成功提交,其所做的修改就应该永久保存到数据库或其他持久化存储中,即使系统出现故障也不应丢失。
四、Spring 事务的实现方式
(一)编程式事务管理
编程式事务管理允许开发者在代码中显式地控制事务的开始、提交和回滚。这种方式提供了最大程度的灵活性,但也使得代码与事务管理逻辑紧密耦合。
以下是一个使用编程式事务管理的示例:
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
public class TransactionExample {
private final PlatformTransactionManager transactionManager;
public TransactionExample(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
public void doTransaction() {
// 定义事务属性
TransactionDefinition def = new DefaultTransactionDefinition();
// 获取事务状态
TransactionStatus status = transactionManager.getTransaction(def);
try {
// 执行业务逻辑操作,这里假设是数据库操作
//...
// 提交事务
transactionManager.commit(status);
} catch (Exception e) {
// 回滚事务
transactionManager.rollback(status);
e.printStackTrace();
}
}
}
在上述示例中,首先创建了一个TransactionDefinition
对象来定义事务的属性,如事务的隔离级别、传播行为等。然后通过PlatformTransactionManager
获取TransactionStatus
,并在try-catch
块中执行业务逻辑。如果业务逻辑执行成功,则提交事务;如果出现异常,则回滚事务。
(二)声明式事务管理
声明式事务管理是 Spring 事务管理的主要方式,它通过在配置文件(XML 或 Java 配置)或使用注解来声明事务的边界,将事务管理与业务逻辑分离,使得代码更加简洁、可维护性更高。
- 基于 XML 配置的声明式事务管理
<?xml version="10.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.driver.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 开启事务注解驱动 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 定义事务代理 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 对指定方法设置事务属性 -->
<tx:method name="transfer" propagation="REQUIRED"/>
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 配置事务代理的切入点 -->
<aop:config>
<aop:pointcut id="serviceOperation" expression="execution(* com.example.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceOperation"/>
</aop:config>
</beans>
在这个 XML 配置中,首先配置了一个DataSourceTransactionManager
作为事务管理器,并通过<tx:annotation-driven>
开启了事务注解驱动。然后使用<tx:advice>
定义了事务的属性,如对于transfer
方法设置了REQUIRED
的传播行为,对于其他方法设置为只读事务。最后通过<aop:config>
配置了事务代理的切入点,将事务应用到com.example.service
包下的所有方法。
- 基于注解的声明式事务管理
import org.springframework.transaction.annotation.Transactional;
@Transactional
public class BankService {
public void transfer(String fromAccount, String toAccount, double amount) {
// 实现转账逻辑,这里涉及数据库操作
//...
}
}
在上述示例中,使用@Transactional
注解标记了BankService
类的transfer
方法,该方法将在事务环境中执行。如果transfer
方法执行过程中出现异常,事务将自动回滚。
五、Spring 事务的传播行为
事务传播行为定义了在一个事务方法被另一个事务方法调用时,事务应该如何传播。Spring 提供了多种传播行为,常用的有以下几种:
(一)REQUIRED(默认)
如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新事务。例如:
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// 业务逻辑操作
methodB();
}
@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
// 业务逻辑操作
}
当methodA
被调用时,如果当前没有事务,则创建一个新事务,然后在methodA
中调用methodB
时,methodB
会加入到methodA
创建的事务中。
(二)REQUIRES_NEW
总是创建一个新事务,如果当前存在事务,则将当前事务挂起。例如:
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// 业务逻辑操作
methodB();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {
// 业务逻辑操作
}
当methodA
被调用时,如果当前没有事务,则创建一个新事务,在methodA
中调用methodB
时,methodB
会创建一个新的独立事务,并且methodA
的事务会被挂起,直到methodB
的新事务完成。
(三)SUPPORTS
如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。例如:
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// 业务逻辑操作
methodB();
}
@Transactional(propagation = Propagation.SUPPORTS)
public void methodB() {
// 业务逻辑操作
}
当methodA
被调用且存在事务时,methodB
会加入到methodA
的事务中;如果methodA
没有事务,则methodB
以非事务方式执行。
(四)NOT_SUPPORTED
总是以非事务方式执行,如果当前存在事务,则将当前事务挂起。例如:
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// 业务逻辑操作
methodB();
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void methodB() {
// 业务逻辑操作
}
当methodA
被调用且存在事务时,methodB
会以非事务方式执行,并且methodA
的事务会被挂起。
(五)MANDATORY
如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。例如:
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// 业务逻辑操作
methodB();
}
@Transactional(propagation = Propagation.MANDATORY)
public void methodB() {
// 业务逻辑操作
}
当methodA
被调用且存在事务时,methodB
会加入到methodA
的事务中;如果methodA
没有事务,则调用methodB
时会抛出异常。
六、Spring 事务的隔离级别
Spring 事务支持多种隔离级别,与数据库的隔离级别相对应,包括:
(一)DEFAULT(默认)
使用数据库默认的隔离级别。不同数据库的默认隔离级别可能不同,例如 MySQL 的默认隔离级别是可重复读(REPEATABLE READ)。
(二)READ_UNCOMMITTED
允许读取未提交的数据,可能会导致脏读、不可重复读和幻读等问题。
(三)READ_COMMITTED
只能读取已提交的数据,可以避免脏读,但可能会出现不可重复读的情况。
(四)REPEATABLE_READ
在同一个事务中多次读取同一数据的结果是相同的,可以避免脏读和不可重复读,但可能会出现幻读。
(五)SERIALIZABLE
最高的隔离级别,事务串行化执行,完全避免了脏读、不可重复读和幻读,但性能开销较大。
可以在@Transactional
注解或 XML 配置中设置事务的隔离级别,例如:
@Transactional(isolation = Isolation.READ_COMMITTED)
public void method() {
// 业务逻辑操作
}
或者在 XML 配置中:
<tx:method name="method" isolation="READ_COMMITTED"/>
七、Spring 事务的回滚规则
默认情况下,Spring 事务会在运行时抛出未检查异常(RuntimeException 及其子类)或 Error 时回滚,而对于检查异常(如 IOException、SQLException 等),事务不会回滚。但可以通过@Transactional
注解的rollbackFor
或noRollbackFor
属性来指定回滚或不回滚的异常类型。
例如:
@Transactional(rollbackFor = Exception.class)
public void method() {
// 业务逻辑操作,如果抛出任何异常都会回滚事务
}
或者
@Transactional(noRollbackFor = IOException.class)
public void method() {
// 业务逻辑操作,即使抛出 IOException 异常也不会回滚事务
}
八、总结
Spring 事务管理为开发者提供了强大而灵活的方式来处理企业级应用中的事务。通过理解 Spring 事务的基本概念、特性、实现方式、传播行为、隔离级别和回滚规则,并结合实际的示例代码进行学习和实践,能够帮助开发者更好地在 Spring 框架下构建可靠、高效的应用程序,确保数据的一致性和完整性,提高系统的稳定性和可维护性。在实际应用中,需要根据具体的业务需求和场景,合理选择事务管理方式、传播行为、隔离级别等参数,以达到最佳的性能和效果。
标签:事务,Java,Spring,Transactional,methodB,methodA,public From: https://blog.csdn.net/weixin_69477306/article/details/144406836