在Spring框架中,声明式事务管理是一种通过注解或配置文件自动管理事务的方式,而不需要手动编写事务管理代码。@Transactional
是Spring提供的一个注解,用于声明式事务管理,它使得事务的管理变得简单而清晰。
主要特性
- 自动事务管理:使用
@Transactional
注解时,Spring会自动为相关方法开启、提交和回滚事务。 - 事务的传播行为:Spring事务支持多种传播行为,控制方法之间如何共享事务。例如:
REQUIRED
(默认值),REQUIRES_NEW
,NESTED
等。 - 事务的隔离级别:Spring事务支持多种隔离级别,如
READ_COMMITTED
,READ_UNCOMMITTED
,SERIALIZABLE
等,用于定义不同事务之间的隔离程度。 - 事务的回滚规则:可以配置在发生哪些异常时进行回滚,默认情况下,Spring只会对
RuntimeException
和Error
进行回滚。 - 事务超时:可以设置事务的超时时间,超时后自动回滚。
使用方式
添加依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
@Transactional
可以用在类或方法上:
- 类级别:在类上使用时,所有方法都会参与事务。
- 方法级别:在方法上使用时,只有该方法会参与事务。
示例代码:
import org.springframework.transaction.annotation.Transactional;
import org.springframework.stereotype.Service;
@Service
public class AccountService {
@Transactional
public void transferMoney(Account fromAccount, Account toAccount, double amount) {
// 执行转账操作
fromAccount.withdraw(amount);
toAccount.deposit(amount);
}
}
- ⽅法/类被 @Transactional 注解修饰时, 在⽬标⽅法执⾏开始之前, 会⾃动开启事务, ⽅法执⾏结束之后, ⾃动提交事务.
- 如果在⽅法执⾏过程中, 出现异常, 且异常未被捕获, 就进⾏事务回滚操作.
- 如果异常被程序捕获, ⽅法就被认为是成功执⾏, 依然会提交事务.
手动回滚事务
TransactionAspectSupport
是 Spring AOP 事务支持的一个类,它提供了对当前事务状态的访问。通过 currentTransactionStatus()
获取当前事务的状态,并调用 setRollbackOnly()
方法标记事务为回滚状态,从而在事务提交时强制回滚。
使用场景
通常在业务逻辑中,如果发生某些特定的异常或需要根据业务条件回滚事务时,可以手动调用 setRollbackOnly()
,这样Spring会在事务提交时回滚事务。
示例代码
假设你有一个业务服务,想根据某些条件决定是否回滚事务:
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
@Service
public class AccountService {
@Transactional
public void transferMoney(Account fromAccount, Account toAccount, double amount) {
// 执行转账操作
fromAccount.withdraw(amount);
toAccount.deposit(amount);
// 根据业务逻辑判断是否回滚事务
if (amount < 0) {
// 如果金额小于0,手动回滚事务
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
}
解释
@Transactional
注解标注了整个方法,表示方法中的操作都在一个事务中执行。- 在方法内部,如果发现金额小于0,就调用
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()
来标记事务为回滚状态。 - 当事务提交时,Spring会发现事务标记为回滚状态,并会自动回滚事务。
在Spring的@Transactional
注解中,isolation
属性用于定义事务的隔离级别。事务的隔离级别决定了一个事务内的操作对其他并发事务的可见性,以及其他事务的操作对当前事务的影响。通过设置适当的隔离级别,可以有效地控制并发事务对数据的一致性和完整性所带来的影响。
事务隔离级别
数据库的事务隔离级别通常定义了一个事务内操作的数据在提交之前是否对其他事务可见。事务隔离级别越高,系统的并发性可能会越低,但数据一致性和安全性会更高。常见的事务隔离级别有四种,它们的定义如下:
READ_UNCOMMITTED
(读取未提交):- 允许事务读取其他事务未提交的数据。
- 可能会导致脏读(Dirty Read),即一个事务读取到另一个事务尚未提交的脏数据。
- 最低的隔离级别,对并发性能有较少影响,但会导致数据不一致。
READ_COMMITTED
(读取已提交):- 只允许读取其他事务已提交的数据。
- 可以避免脏读,但是可能会出现不可重复读(Non-repeatable Read),即在同一个事务内,如果读取相同数据,可能会得到不同的结果,因为其他事务可能已修改了数据。
- 是大多数数据库的默认隔离级别。
REPEATABLE_READ
(可重复读取):- 保证事务内读取的数据在事务执行期间不会被其他事务修改。
- 避免了脏读和不可重复读,但可能会导致幻读(Phantom Read),即事务内的查询结果在执行过程中可能会因为其他事务的插入操作而发生变化。
SERIALIZABLE
(串行化):- 是最高的隔离级别,所有事务按顺序执行,模拟为串行执行。
- 事务之间完全隔离,可以避免脏读、不可重复读和幻读。
- 由于严格的隔离性,会显著降低并发性,可能导致性能瓶颈。
Spring 中的 @Transactional
isolation
属性
在Spring的@Transactional
注解中,isolation
属性允许你指定事务的隔离级别。它的默认值通常是Isolation.DEFAULT
,表示使用底层数据库的默认隔离级别。如果你希望显式指定某个隔离级别,可以使用以下选项:
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.annotation.Isolation;
@Transactional(isolation = Isolation.READ_COMMITTED)
public void someMethod() {
// 业务逻辑
}
Spring 提供的隔离级别选项
Isolation.DEFAULT
:使用底层数据库的默认隔离级别,通常是READ_COMMITTED
。Isolation.READ_UNCOMMITTED
:读取未提交的事务数据,可能会导致脏读。Isolation.READ_COMMITTED
:读取已提交的数据,可以防止脏读。Isolation.REPEATABLE_READ
:可重复读取,防止脏读和不可重复读。Isolation.SERIALIZABLE
:串行化,事务完全隔离,防止脏读、不可重复读和幻读。
示例
假设你有一个银行转账业务,需要确保转账过程中的一致性。你可以使用@Transactional
注解来定义事务的隔离级别,确保在高并发环境下的事务隔离性。
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
public class BankService {
@Transactional(isolation = Isolation.READ_COMMITTED)
public void transferMoney(Account fromAccount, Account toAccount, double amount) {
// 从fromAccount账户中扣款
fromAccount.withdraw(amount);
// 向toAccount账户中存款
toAccount.deposit(amount);
}
}
在上述代码中,@Transactional
注解使用了READ_COMMITTED
隔离级别,确保在转账过程中,fromAccount
和toAccount
的余额在事务提交之前是不可见的。
隔离级别的影响
- 性能:较低的隔离级别(如
READ_UNCOMMITTED
)会提高并发性,但可能会引发脏读、不可重复读等问题。而较高的隔离级别(如SERIALIZABLE
)则会降低并发性,但保证数据的一致性。 - 一致性:较高的隔离级别可以保证更高的数据一致性,避免脏读、不可重复读和幻读。但代价是性能会受影响。
事务传播机制
在Spring的@Transactional
注解中,propagation
属性用于定义事务的传播行为,决定了一个事务方法在调用另一个事务方法时,事务如何传播。事务传播机制影响一个方法调用另一个方法时如何参与事务,决定了是加入现有事务、创建新事务还是不参与事务等行为。
常见的事务传播行为
Propagation.REQUIRED
(默认传播行为):
- 如果当前没有事务存在,则新建一个事务;如果当前存在事务,则加入到现有事务中。
- 这是最常用的传播行为,也是默认行为。通常情况下,如果没有明确的需求,推荐使用此传播行为。
示例:
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// 如果没有事务,启动一个新的事务;如果已经有事务,加入当前事务
}
Propagation.SUPPORTS
:
- 如果当前存在事务,则加入到事务中;如果没有事务,则以非事务的方式执行。
- 当调用方法时,如果存在事务,方法会在事务内执行;如果没有事务,方法不会参与事务。
示例:
@Transactional(propagation = Propagation.SUPPORTS)
public void methodB() {
// 如果没有事务,非事务执行;如果有事务,加入当前事务
}
Propagation.MANDATORY
:
- 如果当前没有事务,则抛出异常;如果当前存在事务,则加入到现有事务中。
- 强制要求方法必须在一个事务中运行。如果没有事务存在,Spring会抛出
IllegalTransactionStateException
异常。
示例:
@Transactional(propagation = Propagation.MANDATORY)
public void methodC() {
// 必须有一个已经存在的事务,如果没有事务,会抛出异常
}
Propagation.REQUIRES_NEW
:
- 无论当前是否存在事务,都会创建一个新的事务;如果当前存在事务,则将当前事务挂起,直到新的事务提交或回滚后恢复。
- 适用于需要独立运行的事务,确保方法执行时有一个新的事务与当前事务分离。
示例:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodD() {
// 创建一个新的事务,挂起当前事务,提交后再恢复当前事务
}
Propagation.NOT_SUPPORTED
:
- 如果当前存在事务,则挂起当前事务,并以非事务的方式执行方法;如果没有事务,方法则以非事务的方式执行。
- 适用于需要禁用事务的场景,无论当前是否有事务,都不使用事务。
示例:
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void methodE() {
// 不使用事务,无论当前是否有事务
}
Propagation.NEVER
:
- 如果当前存在事务,则抛出异常;如果没有事务,则以非事务的方式执行。
- 与
MANDATORY
类似,但NEVER
明确要求方法不在事务中执行。如果当前有事务,Spring会抛出IllegalTransactionStateException
异常。
示例:
@Transactional(propagation = Propagation.NEVER)
public void methodF() {
// 如果当前有事务,则抛出异常;没有事务时,以非事务方式执行
}
Propagation.NESTED
:
- 如果当前存在事务,则在当前事务内创建一个嵌套事务;如果没有事务,则与
REQUIRED
类似,创建一个新的事务。 - 嵌套事务是通过保存点实现的,如果外部事务回滚,嵌套事务也会回滚。否则,嵌套事务可以独立提交。
示例:
@Transactional(propagation = Propagation.NESTED)
public void methodG() {
// 如果存在事务,创建一个嵌套事务;如果没有事务,创建一个新的事务
}
传播行为总结
传播行为 | 描述 |
---|---|
REQUIRED | 默认行为,存在事务时加入事务;不存在时创建新事务。 |
SUPPORTS | 如果存在事务,则加入事务;如果不存在事务,则不使用事务。 |
MANDATORY | 必须有事务,若无事务则抛出异常。 |
REQUIRES_NEW | 创建一个新事务,挂起当前事务。 |
NOT_SUPPORTED | 不使用事务,挂起当前事务(如果存在)。 |
NEVER | 不在事务中执行,若存在事务则抛出异常。 |
NESTED | 如果存在事务,则创建一个嵌套事务;否则创建一个新事务。 |
示例:事务传播行为的使用
假设你有两个方法methodA
和methodB
,你希望methodA
执行时可以自动加入现有事务,而methodB
则需要每次都新建事务,即使methodA
已经有事务存在。
import org.springframework.transaction.annotation.Transactional;
public class TransactionService {
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// 这个方法如果没有事务,会创建新事务,如果有事务,会加入现有事务
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {
// 这个方法总是会创建新的事务,挂起当前事务
}
}
在这个例子中:
- 当你调用
methodA
时,它会尝试使用已有的事务,或者如果没有事务会启动新的事务。 - 调用
methodB
时,无论methodA
是否在事务中执行,methodB
都会启动一个新的事务,并挂起methodA
的事务。