spring 为什么事务在 RuntimeException 上回滚而不是 SQLException
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7125837/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
why does transaction roll back on RuntimeException but not SQLException
提问by climmunk
I have a Spring-managed service method to manage database inserts. It contains multiple insert statements.
我有一个 Spring 管理的服务方法来管理数据库插入。它包含多个插入语句。
@Transactional
public void insertObservation(ObservationWithData ob) throws SQLException
{
observationDao.insertObservation(ob.getObservation());
// aop pointcut inserted here in unit test
dataDao.insertData(ob.getData());
}
I have two unit tests which throw an exception before calling the second insert. If the exception is a RuntimeException, the transaction is rolled back. If the exception is a SQLException, the first insert is persisted.
我有两个单元测试,它们在调用第二个插入之前抛出异常。如果异常是 RuntimeException,则回滚事务。如果异常是 SQLException,则保留第一个插入。
I'm baffled. Can anyone tell me why the transaction does not roll back on a SQLException? Can anyone offer a suggestion how to manage this? I could catch the SQLException and throw a RuntimeException, but that just seems weird.
我很困惑。谁能告诉我为什么事务不会在 SQLException 上回滚?任何人都可以提供如何管理这个的建议吗?我可以捕获 SQLException 并抛出 RuntimeException,但这看起来很奇怪。
回答by skaffman
This is defined behaviour. From the docs:
这是定义的行为。从文档:
Any
RuntimeExceptiontriggers rollback, and any checked Exception does not.
任何
RuntimeException触发器都会回滚,而任何已检查的异常都不会。
This is common behaviour across all Spring transaction APIs. By default, if a RuntimeExceptionis thrown from within the transactional code, the transaction will be rolled back. If a checked exception (i.e. not a RuntimeException) is thrown, then the transaction will not be rolled back.
这是所有 Spring 事务 API 的常见行为。默认情况下,如果RuntimeException从事务代码中抛出a ,事务将被回滚。如果RuntimeException抛出已检查的异常(即不是 a ),则事务将不会回滚。
The rationale behind this is that RuntimeExceptionclasses are generally taken by Spring to denote unrecoverable error conditions.
这背后的基本原理是RuntimeExceptionSpring 通常采用类来表示不可恢复的错误条件。
This behaviour can be changed from the default, if you wish to do so, but how to do this depends on how you use the Spring API, and how you set up your transaction manager.
如果您愿意,可以更改默认行为,但如何执行此操作取决于您如何使用 Spring API 以及如何设置事务管理器。
回答by Justin Lange
For @Transactional, by default, rollback happens for runtime, unchecked exceptions only. Thus, your checked exception SQLExceptiondoes not trigger a rollback of the transaction; the behavior can be configured with the rollbackForand noRollbackForannotation parameters.
对于@Transactional,默认情况下,回滚只发生在运行时,未经检查的异常。因此,您检查的异常SQLException不会触发事务的回滚;可以使用rollbackFor和noRollbackFor注释参数配置行为。
@Transactional(rollbackFor = SQLException.class)
public void insertObservation(ObservationWithData ob) throws SQLException
{
observationDao.insertObservation(ob.getObservation());
// aop pointcut inserted here in unit test
dataDao.insertData(ob.getData());
}
回答by Nathan Hughes
Spring makes extensive use of RuntimeExceptions (including using DataAccessExceptions to wrap SQLExceptions or exceptions from ORMs) for cases where there's no recovering from the exception. It assumes you want to use checked exceptions for cases where a layer above the service needs to be notified of something, but you don't want the transaction to be interfered with.
对于无法从异常中恢复的情况,Spring 广泛使用 RuntimeExceptions(包括使用 DataAccessExceptions 来包装 SQLExceptions 或来自 ORM 的异常)。它假设您希望在需要通知服务之上的层某些事情的情况下使用检查异常,但您不希望事务受到干扰。
If you're using Spring you might as well make use of its jdbc-wrapping libraries and DataAccessException-translating facility, it will reduce the amount of code you have to maintain and provide more meaningful exceptions. Also having a service layer throwing implementation-specific exceptions is a bad smell. The pre-Spring way was to create implementation-agnostic checked exceptions wrapping implementation-specific exceptions, this resulted in a lot of busy-work and a bloated code base. Spring's way avoids those problems.
如果您正在使用 Spring,您不妨使用它的 jdbc 包装库和 DataAccessException 转换工具,它将减少您必须维护的代码量并提供更有意义的异常。还有一个服务层抛出特定于实现的异常是一种不好的味道。Spring 之前的方法是创建与实现无关的检查异常来包装特定于实现的异常,这导致了大量繁忙的工作和臃肿的代码库。Spring 的方式避免了这些问题。
If you want to know why Spring chose to make things work this way, it's probably because they use AOP to add the transaction-handling. They can't change the signature of the method they are wrapping, so checked exceptions aren't an option.
如果你想知道为什么 Spring 选择让事情以这种方式工作,那可能是因为他们使用 AOP 来添加事务处理。他们不能改变他们包装的方法的签名,所以检查异常不是一个选项。

