Spring 事务解析:传播行为、隔离级别、声明式与编程式实战指南

在 Spring 框架里,事务管理是极为关键的功能,它能保证数据操作的一致性与完整性。下面详细介绍 Spring 事务的相关内容:

核心概念

事务的传播行为

传播行为定义了在嵌套事务场景中事务的处理方式。Spring 提供了 7 种传播行为,常见的有:

  • REQUIRED(默认):若当前没有事务,就创建一个新事务;若已有事务,就加入该事务。
  • REQUIRES_NEW:不管当前是否存在事务,都会创建一个新事务,原事务会被挂起。
  • SUPPORTS:如果当前有事务,就加入该事务;如果没有,就以非事务方式执行。
  • NOT_SUPPORTED:以非事务方式执行操作,若当前存在事务,就将其挂起。
  • MANDATORY:要求当前必须存在一个事务,否则会抛出异常。
事务的隔离级别

隔离级别用于处理多事务并发时可能出现的问题。Spring 支持以下隔离级别:

  • DEFAULT:使用底层数据库的默认隔离级别。
  • READ_UNCOMMITTED:允许读取尚未提交的数据变更,可能会导致脏读、不可重复读和幻读。
  • READ_COMMITTED:只允许读取已经提交的数据,可以避免脏读,但不可重复读和幻读仍有可能发生。
  • REPEATABLE_READ:确保在同一个事务中多次读取同一数据的结果是一致的,可以避免脏读和不可重复读,但幻读仍有可能出现。
  • SERIALIZABLE:最高的隔离级别,通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。
事务的回滚规则

默认情况下,Spring 事务只会在遇到运行时异常(RuntimeException 及其子类)和错误(Error)时才会回滚。不过,你也可以通过@Transactional注解的rollbackFor属性来指定需要回滚的异常类型。

声明式事务(推荐)

使用@Transactional注解是实现声明式事务最简单的方式。下面是一个示例:

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserService {

    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    // 声明式事务示例
    @Transactional(
        propagation = Propagation.REQUIRED,
        isolation = Isolation.READ_COMMITTED,
        timeout = 30,
        readOnly = false,
        rollbackFor = {Exception.class}
    )
    public void transferMoney(Long fromUserId, Long toUserId, BigDecimal amount) {
        // 从源账户扣款
        User fromUser = userRepository.findById(fromUserId)
            .orElseThrow(() -> new RuntimeException("User not found"));
        fromUser.setBalance(fromUser.getBalance().subtract(amount));
        userRepository.save(fromUser);

        // 模拟异常
        if (amount.compareTo(BigDecimal.valueOf(1000)) > 0) {
            throw new RuntimeException("Transfer amount too large");
        }

        // 向目标账户入账
        User toUser = userRepository.findById(toUserId)
            .orElseThrow(() -> new RuntimeException("User not found"));
        toUser.setBalance(toUser.getBalance().add(amount));
        userRepository.save(toUser);
    }
}

在这个示例中:

  • @Transactional注解应用在transferMoney方法上,表示该方法在事务环境中执行。
  • propagation属性设置为REQUIRED,意味着如果当前没有事务,会创建一个新事务。
  • isolation属性设置为READ_COMMITTED,使用数据库的默认隔离级别。
  • timeout属性指定事务的超时时间为 30 秒。
  • readOnly属性设置为false,表示这是一个读写事务。
  • rollbackFor属性指定遇到任何异常都要回滚事务。

编程式事务

编程式事务适用于需要在代码中精确控制事务边界的场景。下面是一个使用TransactionTemplate的示例:

import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

@Service
public class AccountService {

    private final TransactionTemplate transactionTemplate;
    private final AccountRepository accountRepository;

    public AccountService(TransactionTemplate transactionTemplate, AccountRepository accountRepository) {
        this.transactionTemplate = transactionTemplate;
        this.accountRepository = accountRepository;
    }

    // 编程式事务示例
    public void updateAccountBalance(Long accountId, BigDecimal amount) {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                try {
                    Account account = accountRepository.findById(accountId)
                        .orElseThrow(() -> new RuntimeException("Account not found"));
                    
                    // 更新账户余额
                    account.setBalance(account.getBalance().add(amount));
                    accountRepository.save(account);
                    
                    // 模拟业务逻辑
                    if (amount.signum() < 0 && account.getBalance().compareTo(BigDecimal.ZERO) < 0) {
                        throw new RuntimeException("Insufficient balance");
                    }
                } catch (Exception e) {
                    // 手动设置事务回滚
                    status.setRollbackOnly();
                    throw e;
                }
            }
        });
    }
}

在这个示例中:

  • 通过TransactionTemplateexecute方法来执行事务操作。
  • doInTransactionWithoutResult方法中定义事务内要执行的业务逻辑。
  • 如果发生异常,使用status.setRollbackOnly()手动设置事务回滚。

配置事务管理器

要使用 Spring 事务,需要配置事务管理器。下面是基于 Java 配置的示例:

import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableTransactionManagement
public class TransactionConfig {

    private final DataSource dataSource;

    public TransactionConfig(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    // 配置事务管理器
    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource);
    }
}

在这个配置中:

  • @EnableTransactionManagement注解启用 Spring 的事务管理功能。
  • DataSourceTransactionManager是基于 JDBC 的事务管理器,适用于使用 JDBC 或 JPA 进行数据访问的场景。

核心传播行为对比表

传播行为英文描述核心逻辑典型场景
REQUIREDSupport a current transaction, create a new one if none exists.当前有事务则加入,无则创建新事务。方法调用链共享同一事务,统一提交 / 回滚。主业务流程(如订单创建 + 库存扣减),需保证整体一致性。
REQUIRES_NEWCreate a new transaction, and suspend the current transaction if one exists.强制创建新事务,挂起外层事务。新事务独立提交 / 回滚,不影响外层事务状态。异步操作、日志记录(需独立事务),或内层操作需与外层隔离(如重试机制)。
SUPPORTSSupport a current transaction, execute non-transactionally if none exists.有事务则加入,无则非事务执行。适合只读或可选事务场景。查询接口(无写操作时避免事务开销),或根据调用方决定是否启用事务。
NOT_SUPPORTEDExecute non-transactionally, suspend the current transaction if one exists.强制非事务执行,挂起外层事务。适合明确不需要事务的操作。性能敏感的只读操作(如缓存查询),或外层事务需释放资源(如避免锁竞争)。
MANDATORYRequires a current transaction, throw an exception if none exists.必须存在外层事务,否则抛异常。确保操作在事务上下文中执行。内部服务方法(如事务内回调),禁止独立调用(防止数据不一致)。
NEVERDo not support a current transaction, throw an exception if one exists.禁止存在事务,否则抛异常。强制非事务执行。绝对禁止事务的场景(如纯计算逻辑),避免意外加入事务。
NESTEDExecute within a nested transaction if a current transaction exists.嵌套事务,通过数据库保存点(Savepoint)实现部分回滚。外层回滚会触发内层回滚,内层回滚不影响外层。复杂业务分支(如主订单 + 子订单),需支持部分失败时回滚子操作,保留主操作结果。

最佳实践

  1. 事务边界控制:事务范围应尽可能小,避免在事务中包含耗时操作,如网络调用、文件读写等。
  2. 异常处理:确保了解事务的回滚规则,必要时使用rollbackFor属性指定需要回滚的异常。
  3. 避免嵌套事务:嵌套事务容易导致意外的回滚行为,尽量使用扁平的事务结构。
  4. 只读事务优化:对于只读操作,使用readOnly = true可以提高性能。
  5. 监控与调优:监控事务性能,调整隔离级别和超时设置以平衡数据一致性和性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值