出现原因
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)
方法)