Spring Boot 声明式事务

发布于:2024-12-19 ⋅ 阅读:(12) ⋅ 点赞:(0)

目录

1. 声明式事务概述

1.1 什么是声明式事务

声明式事务是Spring通过AOP(面向切面编程)实现的事务管理功能。通过@Transactional注解,可以将事务管理代码与业务代码分离,降低代码耦合度。

1.2 基础配置

@Configuration
@EnableTransactionManagement  // 启用事务管理
public class TransactionConfig {
    // 事务管理器配置已经由Spring Boot自动完成
}

2. @Transactional注解详解

2.1 注解属性说明

@Service
public class TransactionAttributesDemo {
    
    @Transactional(
        // 1. 事务传播行为
        propagation = Propagation.REQUIRED,  // 默认值
        
        // 2. 事务隔离级别
        isolation = Isolation.READ_COMMITTED,
        
        // 3. 事务超时时间(秒)
        timeout = 30,
        
        // 4. 是否只读事务
        readOnly = false,
        
        // 5. 回滚规则
        rollbackFor = {BusinessException.class},  // 导致回滚的异常类
        noRollbackFor = {ValidationException.class},  // 不回滚的异常类
        
        // 6. 事务管理器
        transactionManager = "transactionManager"
    )
    public void businessMethod() {
        // 业务逻辑
    }
}

2.2 事务传播行为详解

@Service
@Slf4j
public class TransactionPropagationDemo {
    
    @Autowired
    private UserService userService;
    
    // 1. REQUIRED(默认):支持当前事务,如果不存在则创建新事务
    @Transactional(propagation = Propagation.REQUIRED)
    public void required() {
        // 业务逻辑
    }
    
    // 2. REQUIRES_NEW:创建新事务,挂起当前事务
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void requiresNew() {
        // 业务逻辑
    }
    
    // 3. NESTED:如果当前存在事务,则创建嵌套事务
    @Transactional(propagation = Propagation.NESTED)
    public void nested() {
        // 业务逻辑
    }
    
    // 4. SUPPORTS:支持当前事务,如果不存在则以非事务方式执行
    @Transactional(propagation = Propagation.SUPPORTS)
    public void supports() {
        // 业务逻辑
    }
    
    // 5. NOT_SUPPORTED:以非事务方式执行,如果存在事务则挂起
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void notSupported() {
        // 业务逻辑
    }
    
    // 6. MANDATORY:必须在事务中运行,如果不存在事务则抛出异常
    @Transactional(propagation = Propagation.MANDATORY)
    public void mandatory() {
        // 业务逻辑
    }
    
    // 7. NEVER:以非事务方式执行,如果存在事务则抛出异常
    @Transactional(propagation = Propagation.NEVER)
    public void never() {
        // 业务逻辑
    }
}

2.3 事务隔离级别详解

@Service
public class TransactionIsolationDemo {
    
    // 1. READ_UNCOMMITTED:读未提交
    @Transactional(isolation = Isolation.READ_UNCOMMITTED)
    public void readUncommitted() {
        // 可能读取到其他事务未提交的数据(脏读)
    }
    
    // 2. READ_COMMITTED:读已提交
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void readCommitted() {
        // 只能读取已经提交的数据,但可能出现不可重复读
    }
    
    // 3. REPEATABLE_READ:可重复读
    @Transactional(isolation = Isolation.REPEATABLE_READ)
    public void repeatableRead() {
        // 同一事务内多次读取结果一致,但可能出现幻读
    }
    
    // 4. SERIALIZABLE:串行化
    @Transactional(isolation = Isolation.SERIALIZABLE)
    public void serializable() {
        // 完全串行化执行,性能最差但是最安全
    }
}

3. 事务失效场景

3.1 常见事务失效情况

@Service
public class TransactionInvalidDemo {
    
    // 1. 非public方法
    @Transactional
    private void privateMethod() {  // 事务无效
        // 业务逻辑
    }
    
    // 2. 同类内部调用
    public void outerMethod() {
        innerMethod();  // 事务无效
    }
    
    @Transactional
    public void innerMethod() {
        // 业务逻辑
    }
    
    // 3. 异常被捕获但未抛出
    @Transactional
    public void catchException() {
        try {
            // 业务逻辑
            throw new RuntimeException();
        } catch (Exception e) {
            log.error("异常", e);
            // 异常被吃掉,事务无法回滚
        }
    }
    
    // 4. 数据库引擎不支持事务(如MySQL的MyISAM)
    @Transactional
    public void mysqlEngine() {
        // 使用不支持事务的表引擎,事务无效
    }
    
    // 5. 异常类型不匹配
    @Transactional  // 默认只回滚RuntimeException
    public void exceptionType() throws Exception {
        throw new Exception("检查异常不会回滚");
    }
    
    // 6. 事务传播行为设置错误
    @Transactional(propagation = Propagation.NEVER)
    public void wrongPropagation() {
        // 在已有事务的情况下调用,将抛出异常
    }
    
    // 7. 未被Spring管理
    class NonSpringBean {
        @Transactional
        public void method() {  // 事务无效,因为不是Spring Bean
        }
    }
    
    // 8. 方法final修饰
    @Transactional
    public final void finalMethod() {  // 事务无效,因为final方法无法被代理
        // 业务逻辑
    }
}

4. 事务使用最佳实践

4.1 合理设置事务边界

@Service
@Slf4j
public class TransactionBoundaryDemo {
    
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private OrderRepository orderRepository;
    
    // 错误示例:事务边界太大
    @Transactional
    public void wrongBoundary() {
        // 非事务操作:查询统计
        long count = userRepository.count();
        log.info("用户总数: {}", count);
        
        // 事务操作:创建订单
        createOrder();
        
        // 非事务操作:发送通知
        sendNotification();
    }
    
    // 正确示例:精确的事务边界
    public void rightBoundary() {
        // 非事务操作
        long count = userRepository.count();
        log.info("用户总数: {}", count);
        
        // 事务操作
        createOrderWithTransaction();
        
        // 非事务操作
        sendNotification();
    }
    
    @Transactional
    public void createOrderWithTransaction() {
        // 只包含需要事务的操作
        createOrder();
    }
}

4.2 正确处理事务异常

@Service
@Slf4j
public class TransactionExceptionDemo {
    
    // 1. 定义自定义业务异常
    public class BusinessException extends RuntimeException {
        public BusinessException(String message) {
            super(message);
        }
    }
    
    // 2. 使用明确的回滚规则
    @Transactional(rollbackFor = Exception.class)
    public void handleBusinessException() {
        try {
            // 业务逻辑
            riskyOperation();
        } catch (BusinessException e) {
            // 记录业务异常
            log.error("业务异常", e);
            throw e;  // 重新抛出确保回滚
        } catch (Exception e) {
            // 记录系统异常
            log.error("系统异常", e);
            throw new RuntimeException("系统错误", e);
        }
    }
    
    // 3. 处理特定异常不回滚
    @Transactional(noRollbackFor = {ValidationException.class})
    public void handleValidationException() {
        try {
            // 业务逻辑
            validate();
        } catch (ValidationException e) {
            // 验证异常,记录但不回滚
            log.warn("验证失败", e);
        }
    }
}

5. 实战示例

5.1 转账业务实现

@Service
@Slf4j
public class TransferService {
    
    @Autowired
    private AccountRepository accountRepository;
    
    @Transactional(rollbackFor = Exception.class)
    public void transfer(String fromAccount, String toAccount, BigDecimal amount) {
        // 1. 参数校验
        if (amount.compareTo(BigDecimal.ZERO) <= 0) {
            throw new IllegalArgumentException("转账金额必须大于0");
        }
        
        // 2. 查询账户
        Account from = accountRepository.findByAccountNo(fromAccount)
            .orElseThrow(() -> new BusinessException("转出账户不存在"));
        Account to = accountRepository.findByAccountNo(toAccount)
            .orElseThrow(() -> new BusinessException("转入账户不存在"));
        
        // 3. 余额检查
        if (from.getBalance().compareTo(amount) < 0) {
            throw new BusinessException("余额不足");
        }
        
        try {
            // 4. 执行转账
            from.setBalance(from.getBalance().subtract(amount));
            to.setBalance(to.getBalance().add(amount));
            
            // 5. 保存更改
            accountRepository.save(from);
            accountRepository.save(to);
            
            // 6. 记录交易日志
            logTransaction(from, to, amount);
            
        } catch (Exception e) {
            log.error("转账失败", e);
            throw new BusinessException("转账操作失败");
        }
    }
    
    private void logTransaction(Account from, Account to, BigDecimal amount) {
        // 记录交易日志的逻辑
    }
}

5.2 订单处理实现

@Service
@Slf4j
public class OrderService {
    
    @Autowired
    private OrderRepository orderRepository;
    @Autowired
    private PaymentService paymentService;
    @Autowired
    private InventoryService inventoryService;
    
    @Transactional(rollbackFor = Exception.class)
    public Order createOrder(OrderRequest request) {
        // 1. 创建订单
        Order order = new Order();
        order.setUserId(request.getUserId());
        order.setAmount(calculateAmount(request));
        order.setStatus(OrderStatus.CREATED);
        
        // 2. 保存订单
        order = orderRepository.save(order);
        
        try {
            // 3. 扣减库存(使用REQUIRES_NEW确保独立事务)
            inventoryService.deductInventory(request.getItems());
            
            // 4. 处理支付(使用REQUIRES_NEW确保独立事务)
            paymentService.processPayment(order);
            
            // 5. 更新订单状态
            order.setStatus(OrderStatus.PAID);
            order = orderRepository.save(order);
            
            return order;
        } catch (Exception e) {
            // 6. 异常处理
            order.setStatus(OrderStatus.FAILED);
            orderRepository.save(order);
            throw new BusinessException("订单处理失败", e);
        }
    }
    
    private BigDecimal calculateAmount(OrderRequest request) {
        // 计算订单金额的逻辑
        return BigDecimal.ZERO;
    }
}

关键点:

  1. 正确使用@Transactional注解及其属性
  2. 理解并避免事务失效的场景
  3. 合理设置事务边界和传播行为
  4. 正确处理事务中的异常
  5. 遵循事务使用的最佳实践

在实际开发中,声明式事务能满足大多数场景的需求,它提供了一种优雅的方式来管理事务,使开发者可以专注于业务逻辑的实现。