Spring事务
1.Spring 事务
Spring 事务的核心注解
(1)@Transactional
Spring 通过 @Transactional
来管理事务。
- 通过 AOP(面向切面编程)机制,拦截带有
@Transactional
的方法。 - 在方法执行前开启事务,执行成功后提交事务,执行失败后回滚事务。
@Transactional
public void transferMoney(String fromUser, String toUser, double amount) {
// 从fromUser账户扣除金额
accountDao.debit(fromUser, amount);
// 向toUser账户增加金额
accountDao.credit(toUser, amount);
}
(2)@EnableTransactionManagement
- 启用 Spring 事务管理功能。
- 一般在配置类或启动类中使用:
@Configuration
@EnableTransactionManagement
public class AppConfig {
}
2.Spring 事务的传播机制
从源码来看,一共有 7 种事务传播行为:

REQUIRED:如果当前有事务,就加入;如果没有,就新建一个事务。
REQUIRES_NEW:不管当前有没有事务,都新建一个事务。如果当前有事务,就把当前事务挂起。
如果
methodA
调用methodB
,即使methodA
已经在一个事务中,methodB
也会开启一个新事务,并且methodA
的事务会被挂起,等methodB
的事务完成后,methodA
的事务再继续。
SUPPORTS:如果当前有事务,就加入;如果没有,就以非事务方式执行。
NOT_SUPPORTED:以非事务方式执行,如果当前有事务,就挂起。
MANDATORY:必须在一个已有的事务中执行,否则抛出异常。
NEVER:不能在事务中执行,否则抛出异常。
NESTED:有事务就开嵌套事务,没有就新建。
3.应用场景
REQUIRED:
- 应用场景:常见的业务逻辑调用。比如,在订单创建时调用库存减少的方法,它们都应该共享同一个事务。
- 优点:事务复用,性能开销较小,适用于大多数业务逻辑。
REQUIRES_NEW:
- 应用场景:日志记录、通知服务等。即使主事务失败,独立事务的操作也应该成功执行。
- 优点:事务隔离,防止主要事务的失败影响到辅助操作。
SUPPORTS:
- 应用场景:可选的事务支持。比如,某个方法可以在事务外部或内部执行。
- 优点:灵活处理事务的加入或不加入。
NOT_SUPPORTED:
- 应用场景:需要明确禁止事务的场景,比如读取配置信息、不需要事务控制的数据查询。
- 优点:避免不必要的事务开销。
MANDATORY:
- 应用场景:必须在现有事务中执行的场景。常用于确保方法调用链的一致性。
- 缺点:强依赖外部事务,如果没有事务,则会失败。
NEVER:
- 应用场景:需要保证绝对没有事务的场景,比如某些不允许在事务中执行的数据库操作。
- 缺点:与
MANDATORY
相反,依赖于没有事务的环境。
NESTED:
- 应用场景:需要部分回滚或局部事务的业务逻辑。比如,订单中的部分操作可能会失败,但不希望整个订单回滚。
- 优点:灵活处理子事务,局部回滚而不影响整体事务。
4.事务传播行为使用示例
REQUIRED(默认行为)
REQUIRED
是 Spring 的默认事务传播行为,意味着方法会加入现有事务中,如果没有事务就创建一个新事务。通常用于大多数业务逻辑中,确保整个业务过程处于同一个事务中。
@Service
public class OrderService {
@Autowired
private InventoryService inventoryService;
@Transactional(propagation = Propagation.REQUIRED)
public void placeOrder(Order order) {
// 保存订单
saveOrder(order);
// 调用库存减少操作,使用同一个事务
inventoryService.reduceInventory(order);
}
private void saveOrder(Order order) {
// 保存订单逻辑
}
}
@Service
public class InventoryService {
@Transactional(propagation = Propagation.REQUIRED)
public void reduceInventory(Order order) {
// 减少库存的逻辑
}
}
在这个例子中,OrderService
和 InventoryService
使用了 REQUIRED
,意味着 reduceInventory()
方法和 placeOrder()
方法运行在同一个事务中。如果其中一个方法抛出异常,整个事务都会回滚。
REQUIRES_NEW
REQUIRES_NEW
强制启动一个新事务,无论是否存在当前事务。如果有现有事务,它将被挂起,直到新事务完成。
@Service
public class PaymentService {
@Autowired
private LogService logService;
@Transactional(propagation = Propagation.REQUIRED)
public void processPayment(Order order) {
// 执行支付逻辑
process(order);
// 记录日志,独立事务
logService.logPayment(order);
}
private void process(Order order) {
// 支付处理逻辑
}
}
@Service
public class LogService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logPayment(Order order) {
// 记录支付日志逻辑,使用独立事务
}
}
在这个例子中,PaymentService
的 processPayment()
和 LogService
的 logPayment()
在不同的事务中执行。如果 processPayment()
方法抛出异常,logPayment()
仍然会提交,因为它运行在独立的事务中。
SUPPORTS
SUPPORTS
表示方法可以支持事务,但不强制要求。如果当前有事务,它将在事务中执行;如果没有事务,它将以非事务方式执行。
@Service
public class AccountService {
@Transactional(propagation = Propagation.REQUIRED)
public void transferFunds(Account fromAccount, Account toAccount, BigDecimal amount) {
// 转账逻辑
withdraw(fromAccount, amount);
deposit(toAccount, amount);
}
@Transactional(propagation = Propagation.SUPPORTS)
public BigDecimal getBalance(Account account) {
// 获取余额,如果存在事务,则在事务中执行;否则无事务执行
return account.getBalance();
}
private void withdraw(Account fromAccount, BigDecimal amount) {
// 扣款逻辑
}
private void deposit(Account toAccount, BigDecimal amount) {
// 存款逻辑
}
}
getBalance()
方法可以在事务内或非事务中执行。如果 transferFunds()
调用了 getBalance()
,它将在 REQUIRED
事务中执行;否则,它以非事务方式执行。
NOT_SUPPORTED
NOT_SUPPORTED
表示该方法不支持事务,如果当前有事务,它将挂起该事务,并在非事务环境中执行。
@Service
public class ReportService {
@Transactional(propagation = Propagation.REQUIRED)
public void generateMonthlyReport() {
// 生成月度报告,支持事务
generateReportData();
// 发送报告邮件,无事务
sendReportEmail();
}
private void generateReportData() {
// 生成报告数据逻辑
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void sendReportEmail() {
// 发送邮件逻辑,挂起现有事务
}
}
在这个例子中,generateMonthlyReport()
方法中的 generateReportData()
运行在事务中,而 sendReportEmail()
运行在非事务环境中,即使 generateMonthlyReport()
已经有事务,它也会挂起现有事务。
MANDATORY
MANDATORY
表示该方法必须在已有事务中运行,如果没有现有事务则抛出异常。
@Service
public class TransactionalService {
@Transactional(propagation = Propagation.REQUIRED)
public void performAction() {
// 执行主要操作
performSecondaryAction();
}
@Transactional(propagation = Propagation.MANDATORY)
public void performSecondaryAction() {
// 必须在事务内执行
}
}
在这个例子中,performSecondaryAction()
必须在一个事务中运行。如果它被独立调用而没有事务,则抛出异常。如果 performAction()
方法已经启动了一个事务,那么 performSecondaryAction()
可以正常运行。
NEVER
NEVER
表示该方法不允许在事务中运行,如果当前存在事务,则抛出异常。
@Service
public class TransactionFreeService {
@Transactional(propagation = Propagation.NEVER)
public void executeWithoutTransaction() {
// 执行逻辑,不能在事务中执行
}
}
如果在事务上下文中调用 executeWithoutTransaction()
方法,会抛出异常,因为它要求不在任何事务中执行。
NESTED
NESTED
表示如果当前存在事务,则在该事务中创建一个嵌套事务(支持保存点),子事务可以独立回滚而不影响主事务。
@Service
public class NestedTransactionService {
@Transactional(propagation = Propagation.REQUIRED)
public void mainOperation() {
// 执行主操作
try {
nestedOperation();
} catch (Exception e) {
// 嵌套事务回滚,不影响主事务
}
}
@Transactional(propagation = Propagation.NESTED)
public void nestedOperation() {
// 嵌套事务操作
// 可以在这里回滚嵌套事务
throw new RuntimeException("嵌套事务回滚");
}
}
在这个例子中,mainOperation()
是主事务,而 nestedOperation()
是嵌套事务。如果 nestedOperation()
失败,它只会回滚嵌套事务,主事务 mainOperation()
不会受影响。
5.事务的隔离级别
①、DEFAULT:使用数据库默认的隔离级别(你们爱咋咋滴 😁),MySQL 默认的是可重复读,Oracle 默认的读已提交。
②、READ_UNCOMMITTED:读未提交,允许事务读取未被其他事务提交的更改。这是隔离级别最低的设置,可能会导致“脏读”问题。
③、READ_COMMITTED:读已提交,确保事务只能读取已经被其他事务提交的更改。这可以防止“脏读”,但仍然可能发生“不可重复读”和“幻读”问题。
④、REPEATABLE_READ:可重复读,确保事务可以多次从一个字段中读取相同的值,即在这个事务内,其他事务无法更改这个字段,从而避免了“不可重复读”,但仍可能发生“幻读”问题。
⑤、SERIALIZABLE:串行化,这是最高的隔离级别,它完全隔离了事务,确保事务序列化执行,以此来避免“脏读”、“不可重复读”和“幻读”问题,但性能影响也最大。
它们定义在 TransactionDefinition 接口中。
隔离级别与脏读、不可重复读、幻读表格汇总
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
READ_UNCOMMITTED | 是 | 是 | 是 |
READ_COMMITTED | 否 | 是 | 是 |
REPEATABLE_READ | 否 | 否 | 是 |
SERIALIZABLE | 否 | 否 | 否 |
在 Spring 中,使用 @Transactional
注解可以方便地设置事务的隔离级别。例如:
@Transactional(isolation = Isolation.REPEATABLE_READ)
public void processTransaction() {
// 事务逻辑
}
选择隔离级别需要根据应用的具体需求进行权衡:
- 低隔离级别(READ_UNCOMMITTED 和 READ_COMMITTED):性能高,但可能存在并发问题,适合数据一致性要求不高的场景。
- 高隔离级别(REPEATABLE_READ 和 SERIALIZABLE):数据一致性强,但性能较差,适合高数据一致性要求的场景。