在Spring 3 / Hibernate中回滚事务的最佳实践

引用Spring文档 :

任何RuntimeException都会触发回滚,任何已检查的Exception都不会

引用javapractices.com

未经检查的例外情况:

  • 表示程序中的缺陷(错误) – 通常是传递给非私有方法的无效参数。 引用来自Gosling,Arnold和Holmes的Java编程语言:“未经检查的运行时exception表示一般来说,它反映了程序逻辑中的错误,并且无法在运行时合理地恢复。”
  • 是RuntimeException的子类,通常使用IllegalArgumentException,NullPointerException或IllegalStateException实现
  • 方法没有义务为其实现抛出的未经检查的exception建立策略(并且它们几乎总是不这样做)

检查exception:

  • 代表程序直接控制范围之外的区域中的无效条件(无效的用户输入,数据库问题,网络中断,缺少文件)
  • 是Exception的子类
  • 方法有义务为其实现抛出的所有已检查exception建立策略(将已检查的exception传递到堆栈的更高位置,或以某种方式处理它)

如果在我的业务逻辑中发现问题并且我想要回滚更改,我必须抛出一个新的RuntimeException? 它不是真正的RuntimeException(未经检查的exception),因为我已经在逻辑中识别出它。 或许我误解了这些概念?

我真正的问题是 ,在我的@Transactional服务方法中回滚事务的最佳做法是什么?

如果您正在使用已检查的exception,则只需将它们添加到@Transactional注释的rollbackFor属性中。

 @Transactional(rollbackFor = { MyInvalidUserException.class, MyApplicationException.class }) public void method() throws MyInvalidUserException, MyApplicationException { ... ... } 

等等

org.life.java的答案也很好。 如果您想将程序化事务管理混合到声明性事务中或将其严格声明,则这是一个学术决策。

从内部以编程方式回滚:

 @Transactional public void commit() { try { // some business logic... } catch (ConstraintViolationException e) { // trigger rollback programmatically TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } } 

或标记回滚的exception并从调用者处理它:

 @Transactional(rollBackFor = TransactionException.class) public void commit() throws ConstraintViolationException{ try { // some business logic... } catch (ConstraintViolationException e) { // handle the exception // re-throw for rollback new TransactionException(e); } } public void doCommit(){ try { commit() } catch (TransactionException e){ // do nothing as already handled } } 

我更喜欢前者,因为它使代码更简单,但根据Spring文档,它是不鼓励的 :

如果您绝对需要程序化回滚,则可以使用程序化回滚,但它的使用方式可以实现基于POJO的简洁体系结构。

应该是这样的

 @Transactional public void method () throws YourCustomException { try{ //logic }catch(Exception ex){ TransactionAspectSupport.currentTransactionStatus() .setRollbackOnly(); throw(new YourCustomException(ex.getMessage())); } } 

我认为可以肯定地说,应该检查哪些例外有不同的意见。 例如,请考虑Spring Framework简介中的摘录:

Spring数据访问exception层次结构基于未经检查(运行时)exception。 在几个项目上与Spring合作后,我越来越相信这是正确的决定。

数据访问exception通常不可恢复。 例如,如果我们无法连接到数据库,则特定业务对象不太可能解决该问题。 一个潜在的例外是乐观锁定违规,但并非所有应用程序都使用乐观锁定。 通常很难被编写代码来捕获无法合理处理的致命exception。 让它们传播到像servlet或EJB容器这样的顶级处理程序通常更合适。 所有Spring数据访问exception都是DataAccessException的子类,因此如果我们选择捕获所有Spring数据访问exception,我们就可以轻松实现。

请注意,如果我们确实想从未经检查的数据访问exception中恢复,我们仍然可以这样做。 我们可以编写代码来处理可恢复的条件。 例如,如果我们认为只有乐观锁定违规是可恢复的,我们可以在Spring DAO中编写代码,如下所示:

try {// do work} catch(OptimisticLockingFailureException ex){//我对此感兴趣}如果检查了Spring数据访问exception,我们需要编写以下代码。 请注意,我们仍然可以选择写这个:

try {// do work} catch(OptimisticLockingFailureException ex){//我对此感兴趣} catch(DataAccessException ex){//致命; 只是重新抛出它}第一个例子的一个潜在的反对意见 – 编译器无法强制处理潜在的可恢复exception – 也适用于第二个例子。 因为我们被迫捕获基本exception(DataAccessException),所以编译器不会强制检查子类(OptimisticLockingFailureException)。 因此,编译器会强制我们编写代码来处理不可恢复的问题,但在强制我们处理可恢复的问题方面没有任何帮助。

Spring使用未经检查的数据访问exception与许多(可能是大多数)成功的持久性框架的使用一致。 (实际上,它部分受到JDO的启发。)JDBC是少数使用已检查exception的数据访问API之一。 例如,TopLink和JDO仅使用未经检查的exception。 Hibernate在版本3中从已检查到未检查的exception切换。

数据访问exception显然不在程序的直接控制之内,因此根据javapractices,应该检查它们。 但春源的人不同。 我相信他们的判断不仅仅是javapractices。

因此,我认为抛出未经检查的exception以指示应该回滚事务没有错。 当然,您也可以使用已检查的exception,并配置方面以回滚它们。 (详见Affe的答案)

遇到这个URL来处理已检查和未检查的exception,管理事务:这不是注释驱动的方法,但它很好。

http://javasight.wordpress.com/2009/02/17/understanding-spring-transaction-management-with-checked-and-unchecked-exceptions/