本文目录导读:

Java事务回滚的规范流程,核心在于确保资源被正确释放、异常被妥善处理,并且回滚条件清晰明确。
事务管理分为编程式事务(手动控制)和声明式事务(基于AOP注解),下面从这两种方式出发,结合最佳实践,总结规范流程。
核心原则
- 明确边界:事务开始和结束的边界必须清晰,通常是“Service层”。
- 异常驱动:默认情况下,运行时异常(RuntimeException及其子类) 触发回滚,受检异常(Exception的子类,非RuntimeException)默认不触发回滚。
- 资源管理:数据库连接、会话等资源必须在finally块或使用try-with-resources确保关闭。
- 幂等与状态:失败回滚后,业务流程应能安全重试,避免脏数据残留。
声明式事务(最常用,推荐)
通过@Transactional注解,由Spring AOP自动管理事务的提交和回滚。
规范流程
- 在Service层(而非Controller层)开启事务。
- 设置合理的rollbackFor,明确哪些异常需要回滚。
- 在方法内部捕获异常并处理时,若需要触发回滚,需重新抛出注解指定的异常。
- 避免事务嵌套陷阱(如
try-catch吞掉了异常)。
示例代码
@Service
public class OrderService {
@Autowired
private OrderDao orderDao;
@Autowired
private AccountDao accountDao;
/**
* 创建订单并扣减库存
*
* @param order 订单信息
* @throws BusinessException 业务异常(受检异常,不会自动回滚)
*/
@Transactional(rollbackFor = Exception.class) // 明确指定:所有Exception都回滚
public void createOrder(Order order) throws BusinessException {
LogUtil.info("开始事务:创建订单");
try {
// 1. 插入订单记录(操作数据库)
orderDao.insert(order);
// 2. 扣减库存(可能抛RuntimeException,如除零、空指针等)
stockService.deductStock(order.getProductId(), order.getQuantity());
// 3. 调用外部服务(可能抛受检异常)
// 注意:这里如果捕获异常,必须重新抛出,否则事务不会回滚
boolean result = externalService.syncToERP(order);
if (!result) {
// 抛出受检异常,但因为rollbackFor=Exception.class,会回滚
throw new BusinessException("同步ERP失败");
}
// 4. 事务正常结束,自动提交
LogUtil.info("事务提交");
} catch (Exception e) {
// 记录日志,并重新抛出(让AOP感知到异常,触发回滚)
LogUtil.error("事务执行失败,即将回滚", e);
throw e; // 关键!不能吞掉异常
}
}
}
声明式事务回滚的三种正确写法
| 场景 | 推荐做法 | 说明 |
|---|---|---|
| 运行时异常回滚 | 不捕获异常,或捕获后重新抛出 | 默认rollbackFor=RuntimeException |
| 受检异常回滚 | 注解上设置rollbackFor=Exception.class |
同时注意捕获后重新抛出 |
| 局部不执行回滚 | 使用@Transactional(noRollbackFor=...) |
明确指定某些异常不触发回滚(不常用) |
常见错误:事务不回滚
// ❌ 错误:吞掉了异常,Spring感知不到失败,会正常提交
@Transactional
public void createOrder(Order order) {
try {
orderDao.insert(order);
stockService.deductStock(order.getProductId(), order.getQuantity());
} catch (Exception e) {
LogUtil.error("出错了但我不抛出,事务不会回滚", e);
// 没有throw e,相当于告诉Spring:一切正常,提交!
}
}
编程式事务(手动控制)
适用于需要细粒度控制、或者远程调用、异步场景。
规范流程
- 获取事务管理器(PlatformTransactionManager 或 TransactionTemplate)。
- 定义事务属性(隔离级别、传播行为、回滚规则)。
- 手动commit或rollback。
- 务必确保commit/rollback只执行一次,且资源被释放。
示例代码(使用TransactionTemplate - 推荐)
TransactionTemplate是模板方法模式,内部自动处理了try-catch-finally和回滚逻辑。
@Service
public class PaymentService {
@Autowired
private TransactionTemplate transactionTemplate;
@Autowired
private PaymentDao paymentDao;
public void processPayment(Payment payment) {
LogUtil.info("开始编程式事务");
// 定义执行逻辑
PaymentExecuteResult result = transactionTemplate.execute(new TransactionCallback<PaymentExecuteResult>() {
@Override
public PaymentExecuteResult doInTransaction(TransactionStatus status) {
try {
// 1. 数据库操作
paymentDao.insert(payment);
// 2. 业务逻辑
// ...
// 3. 如果业务判定需要回滚,可以手动调用
if (someConditionFailed) {
status.setRollbackOnly(); // 标记为只回滚
}
return new PaymentExecuteResult(true, "成功");
} catch (Exception e) {
// 异常发生时,TransactionTemplate会自动调用status.setRollbackOnly()
LogUtil.error("编程式事务执行失败", e);
// 如果return null,则事务会回滚(因为抛出异常)
// 如果catch后不重新抛出,需要手动setRollbackOnly
status.setRollbackOnly(); // 显式标记回滚
return new PaymentExecuteResult(false, "失败: " + e.getMessage());
}
}
});
LogUtil.info("事务执行结果:{}", result);
}
}
手动获取事务管理器(不常用,仅用于特殊场景)
@Autowired
private PlatformTransactionManager transactionManager;
// 定义事务定义
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
def.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);
def.setTimeout(30); // 超时设置
TransactionStatus status = transactionManager.getTransaction(def);
try {
// 业务逻辑
// ...
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
throw e;
}
// 不需要finally中重复rollback,commit或rollback只能执行一次
事务回滚的规范检查清单
每一项都很关键,建议在代码审查时重点关注:
- 异常处理:是否吞没了异常?(
catch(Exception e)中没有throw e或TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()) - 注解位置:
@Transactional是否放在Service层的方法上?(Controller层开启事务是反模式) - 传播行为:内部方法调用是否使用了
@Transactional(propagation=REQUIRES_NEW)导致子事务独立? - 资源关闭:数据库连接、文件流是否在 finally 块中关闭?或者使用了
try-with-resources? - 大事务拆分:单个事务内是否包含了耗时操作(如网络IO、文件下载)?如果包含,会长时间占用数据库连接,增加死锁和超时风险。
- 嵌套事务:是否存在
ServiceA.methodA()中调用ServiceB.methodB(),且两个方法都有@Transactional?methodB抛异常,methodA的事务是否回滚,取决于传播行为。 - 测试验证:是否有单元测试或手动测试验证异常场景下数据未被修改?
典型错误与正确做法对比
| 场景 | ❌ 错误做法 | ✅ 正确做法 |
|---|---|---|
| 捕获异常后不抛出 | try {} catch (Exception e) {log.error();} |
catch (Exception e) {log.error(); throw e;} 或 status.setRollbackOnly() |
| 受检异常不回滚 | @Transactional 不设置 rollbackFor |
@Transactional(rollbackFor=Exception.class) |
| 在Controller层开启事务 | @Transactional 放在 @RequestMapping 方法上 |
移到 Service层方法上 |
| 事务内包含远程调用 | @Transactional 包裹 httpClient.doPost() |
将远程调用移出事务,或使用编程式事务控制回滚范围 |
| 多数据源 | 只配置了一个事务管理器 | 配置 @Transactional("dataSource1TransactionManager") 指定对应管理器 |
规范的Java事务回滚流程可以概括为:
- 使用声明式事务(
@Transactional),放置在Service层方法上。 - 明确指定回滚异常类型(特别是受检异常,需要设置
rollbackFor)。 - 在业务逻辑中,如果捕获了异常,必须重新抛出或将事务标记为
rollbackOnly,否则事务不会回滚。 - 使用
TransactionTemplate处理编程式事务,避免手动commit/rollback带来的遗漏风险。 - 避免事务内包含耗时操作,防止长事务。
- 每次修改后,通过交易验证(回滚后数据状态恢复)来确保正确性。
遵循这些规范,可以显著减少因事务回滚不一致导致的数据错误。
版权声明: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直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播