Java回滚案例

wen java案例 1

Java事务回滚案例详解

基本概念

事务回滚是指当程序执行过程中发生异常或错误时,将数据库恢复到事务开始前的状态,保证数据的一致性和完整性。

Java回滚案例

常见回滚场景案例

转账操作(经典案例)

@Service
public class TransferService {
    @Autowired
    private AccountMapper accountMapper;
    /**
     * 转账操作
     * @param fromAccountId 转出账户
     * @param toAccountId 转入账户
     * @param amount 转账金额
     */
    @Transactional(rollbackFor = Exception.class)
    public void transfer(Long fromAccountId, Long toAccountId, BigDecimal amount) {
        // 1. 检查转出账户余额
        Account fromAccount = accountMapper.selectById(fromAccountId);
        if (fromAccount.getBalance().compareTo(amount) < 0) {
            throw new RuntimeException("余额不足");
        }
        // 2. 扣减转出账户余额
        fromAccount.setBalance(fromAccount.getBalance().subtract(amount));
        accountMapper.updateById(fromAccount);
        // 3. 增加转入账户余额(这里可能出现异常)
        Account toAccount = accountMapper.selectById(toAccountId);
        if (toAccount == null) {
            throw new RuntimeException("转入账户不存在");
        }
        toAccount.setBalance(toAccount.getBalance().add(amount));
        accountMapper.updateById(toAccount);
        // 4. 记录转账日志
        transferLogService.saveLog(fromAccountId, toAccountId, amount);
    }
}

批量数据处理

@Service
public class BatchProcessService {
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private StockMapper stockMapper;
    /**
     * 批量处理订单并扣减库存
     */
    @Transactional(rollbackFor = Exception.class)
    public void processOrders(List<OrderDTO> orders) {
        for (OrderDTO order : orders) {
            try {
                // 处理单个订单
                processSingleOrder(order);
            } catch (Exception e) {
                log.error("处理订单失败,订单号: {}", order.getOrderNo(), e);
                // 记录失败原因,继续处理其他订单
                order.setStatus("FAIL");
                order.setErrorMsg(e.getMessage());
                // 可选:将失败订单记录到单独的表
                failedOrderService.saveFailedOrder(order);
            }
        }
    }
    private void processSingleOrder(OrderDTO order) {
        // 更新订单状态
        order.setStatus("PROCESSING");
        orderMapper.updateStatus(order);
        // 扣减库存
        Stock stock = stockMapper.selectByProductId(order.getProductId());
        if (stock.getQuantity() < order.getQuantity()) {
            throw new RuntimeException("库存不足");
        }
        stock.setQuantity(stock.getQuantity() - order.getQuantity());
        stockMapper.updateById(stock);
        // 模拟其他业务处理
        otherBusinessProcess(order);
    }
}

手动回滚(编程式事务)

@Service
public class ManualRollbackService {
    @Autowired
    private TransactionTemplate transactionTemplate;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RoleMapper roleMapper;
    /**
     * 使用编程式事务实现手动回滚
     */
    public void createUserWithRoles(String userName, List<String> roleIds) {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                try {
                    // 1. 创建用户
                    User user = new User();
                    user.setName(userName);
                    userMapper.insert(user);
                    // 2. 为用户分配角色
                    for (String roleId : roleIds) {
                        UserRole userRole = new UserRole();
                        userRole.setUserId(user.getId());
                        userRole.setRoleId(roleId);
                        userRoleMapper.insert(userRole);
                    }
                    // 模拟业务校验失败
                    if (roleIds.size() > 5) {
                        // 手动回滚
                        status.setRollbackOnly();
                        throw new RuntimeException("用户角色数量不能超过5个");
                    }
                } catch (Exception e) {
                    log.error("创建用户失败", e);
                    status.setRollbackOnly();
                    throw e;
                }
            }
        });
    }
}

回滚配置详解

@Transactional 常用参数

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Transactional {
    // 指定事务管理器
    String value() default "";
    // 回滚的异常类型
    Class<? extends Throwable>[] rollbackFor() default {};
    // 不回滚的异常类型
    Class<? extends Throwable>[] noRollbackFor() default {};
    // 事务传播行为
    Propagation propagation() default Propagation.REQUIRED;
    // 事务隔离级别
    Isolation isolation() default Isolation.DEFAULT;
    // 超时时间
    int timeout() default -1;
    // 是否只读事务
    boolean readOnly() default false;
}

回滚失效场景及解决方案

@Service
public class RollbackFailureCases {
    @Autowired
    private UserMapper userMapper;
    /**
     * 错误示例1:异常被捕获,事务不会回滚
     */
    @Transactional
    public void wrongCase1() {
        try {
            // 业务操作
            userMapper.insert(new User("test1"));
            // 模拟异常
            int i = 1/0;
        } catch (Exception e) {
            // 异常被捕获,事务不会回滚
            log.error("发生异常,但不会回滚", e);
        }
        // 事务会在方法结束时提交,前面的插入操作会被保存
    }
    /**
     * 错误示例2:内部调用导致事务失效
     */
    @Transactional
    public void wrongCase2() {
        // 调用本类方法不会开启新事务
        this.updateUser();
        // 下面的操作如果发生异常,updateUser中已提交的操作不会回滚
        int i = 1/0;
    }
    /**
     * 正确做法:事务方法调用
     */
    @Service
    public static class UserService {
        @Transactional
        public void correctCase() {
            // 调用其他类的方法可以开启新事务
            userService2.updateUser();
            // 下面的操作发生异常,updateUser中的操作也会回滚
            int i = 1/0;
        }
    }
}
/**
 * 解决方案
 */
@Service
public class RollbackSolutionService {
    @Autowired
    private SelfProxy selfProxy;
    @Transactional(rollbackFor = Exception.class)
    public void correctMethod() {
        // 1. 捕获异常后重新抛出
        try {
            // 业务操作
            userMapper.insert(new User("test1"));
            int i = 1/0;
        } catch (Exception e) {
            log.error("业务处理失败", e);
            throw new RuntimeException("业务处理失败", e);  // 重新抛出异常
        }
    }
    /**
     * 使用代理调用方法解决自调用问题
     */
    @Transactional
    public void useProxy() {
        // 通过代理调用
        selfProxy.updateUser();
        // 后续操作
        int i = 1/0;
    }
}

常见回滚异常测试

@SpringBootTest
public class RollbackTest {
    @Autowired
    private TransferService transferService;
    @Test
    public void testTransferRollback() {
        BigDecimal originalFromBalance = accountMapper.selectById(1L).getBalance();
        BigDecimal originalToBalance = accountMapper.selectById(2L).getBalance();
        try {
            transferService.transfer(1L, 2L, new BigDecimal("1000"));
        } catch (Exception e) {
            // 验证转账失败后余额不变
            BigDecimal afterFromBalance = accountMapper.selectById(1L).getBalance();
            BigDecimal afterToBalance = accountMapper.selectById(2L).getBalance();
            assertEquals(originalFromBalance, afterFromBalance);
            assertEquals(originalToBalance, afterToBalance);
        }
    }
}

最佳实践建议

  1. 合理设置 rollbackFor:建议设置为 rollbackFor = Exception.class
  2. 避免事务过大:将大事务拆分为小事务
  3. 避免在事务中做耗时操作:如网络请求、文件操作等
  4. 正确处理受检异常:默认不回滚,需提前抛出或转换
  5. 注意事务失效场景:自调用、方法非public、类未被Spring管理等情况

这个案例涵盖了Java事务回滚的主要场景和常见问题解决方案,在实际开发中,要根据具体业务需求选择合适的事务处理方式。

抱歉,评论功能暂时关闭!