出现原因
Spring 框架的默认事务传播方式是 PROPAGATION_REQUIRED (内层事务加入外层事务中)
在内层事务因异常结束时, Spring 会把事务标记为 rollback-only. 这时如果外层事务 catch 捕捉了异常 e, 那么外层事务方法还会继续执行代码, 直到外层事务结束.
Spring 发现事务已经被标记为 rollback-only, 外层方法却正常执行成功, 这时 Spring 就会抛出 UnexpectedRollbackException
org.springframework.transaction.UnexpectedRollbackException:
Transaction rolled back because it has been marked as rollback-only
解决方案
- 希望内层事务抛出异常时 中断程序执行, 在外层事务的 catch 代码块中继续抛出异常 e
- 如果希望程序正常执行完, 并且希望外层事务结束时 全部提交, 则在内层事务方法中将异常 e 捕获并处理
- 如果希望 内层事务回滚, 但不影响外层事务提交, 需要将内层事务的传播方式指定为
PROPAGATION_NESTED. (ps:PROPAGATION_NESTED基于数据库savepoint实现的嵌套事务, 外层事务的提交和回滚能够控制嵌内层事务, 而内层事务报错时, 可以返回原始savepoint, 外层事务可以继续提交.
Java 异常

-
受检异常 checked exception vs 非受检异常 unchecked exception
-
(compile-time) Exceptions (checked) and RuntimeExceptions (unchecked)
-
common checked exceptions in Java are IOException, SQLException and ParseException
-
unchecked exception reflects some error inside the program logic like ArithmeticException
- The RuntimeException class is the superclass of all unchecked exceptions
-
It’s a good practice to use exceptions in Java so that we can separate error-handling code from regular code
- If a client can reasonably be expected to recover from an exception, make it a checked exception (compile)
- If a client cannot do anything to recover from the exception, make it an unchecked exception (runtime)
-
exception vs error
- You can, and probably should, recover from an exception
- You can, but should not, recover from an error
-
本质上 unchecked 和 runtime 是同义词, 但…

-
Spring 默认会对 RuntimeException 和 Error 异常进行回滚
-
Spring 没有对非运行时异常(检查时异常)进行处理,这是因为非运行时异常在编码时,是需要我们开发人员手动去进行 try/catch 进行处理的,也不允许抛出非运行时异常,比如 IOException,不然编译器编译都通不过,更别谈运行程序
-
除了 IOException 等需要 try/catch 的异常, 还有一些不需要 try/catch 处理但是也不属于 RuntimeException 的异常, 比如: SQLException,尽管 JdbcTemplate 会将 SQLException 转为 BadSqlGrammarException (具体可以参考
translateException(String task, @Nullable String sql, SQLException ex)方法)
手动事务
- 编程式事务管理 (TransactionTemplate 实现)
private final TransactionTemplate transactionTemplate;
private final OrderDao orderDao;
@Autowired
public OrderService(PlatformTransactionManager transactionManager, OrderDao orderDao) {
this.transactionTemplate = new TransactionTemplate(transactionManager);
this.orderDao = orderDao;
}
public void createOrder(Order order) {
transactionTemplate.execute(status -> {
try {
orderDao.save(order); // 保存订单
inventoryDao.deductStock(); // 扣减库存
return true; // 提交事务 (正常返回, 无需处理)
} catch (Exception e) {
status.setRollbackOnly(); // 标记回滚
return false;
}
});
}
- PlatformTransactionManager
public void updateData() {
TransactionDefinition def = new DefaultTransactionDefinition();
def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
def.setTimeout(30); // 超时30秒
TransactionStatus status = transactionManager.getTransaction(def); // get 就是开启事务
try {
orderDao.save(order); // 保存订单
inventoryDao.deductStock(); // 扣减库存
transactionManager.commit(status); // 提交事务
} catch (Exception e) {
transactionManager.rollback(status); // 回滚事务
throw new RuntimeException("Transaction rolled back", e);
}
}
- 手动回滚 (在声明式事务中手动回滚)
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
Spring框架默认事务传播为PROPAGATION_REQUIRED,当内层事务异常时会被标记为rollback-only。如果外层事务捕获异常并继续执行,Spring会在事务提交时抛出UnexpectedRollbackException。解决方案包括正确处理异常和调整事务传播行为,例如使用PROPAGATION_NESTED实现嵌套事务。在Java中,受检异常需在编译时处理,非受检异常(如RuntimeException)通常表示程序逻辑错误。Spring对RuntimeException和Error进行回滚,但不对非运行时异常自动处理。

254

被折叠的 条评论
为什么被折叠?



