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);
}
}
}
最佳实践建议
- 合理设置 rollbackFor:建议设置为
rollbackFor = Exception.class - 避免事务过大:将大事务拆分为小事务
- 避免在事务中做耗时操作:如网络请求、文件操作等
- 正确处理受检异常:默认不回滚,需提前抛出或转换
- 注意事务失效场景:自调用、方法非public、类未被Spring管理等情况
这个案例涵盖了Java事务回滚的主要场景和常见问题解决方案,在实际开发中,要根据具体业务需求选择合适的事务处理方式。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播