嵌套的@Transactional方法中的Spring UnexpectedRollbackException

时间:2016-11-08 12:16:30

标签: java spring hibernate transactions

我有一个dao类(MyDao),它标有@Transactional注释(在类级别),没有其他参数。在这个dao类中,我有一个方法,在某些情况下需要抛出一个已检查的异常并执行事务回滚。像这样:

@Transactional
public class MyDao {

    public void daoMethod() throws MyCheckedException() {

        if (somethingWrong) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            throw new MyCheckedException("something wrong");
        }
}

这完全没问题。但是,从服务方法调用此dao方法,该方法也标记为@Transactional:

public class MyService {

    @Autowired
    private MyDao myDao;

    @Transactional
    public void serviceMethod() throws MyCheckedException {
        myDao.daoMethod();
    }

}

问题是,当从daoMethod()调用serviceMethod()并将事务标记为仅回滚时,我会得到UnexpectedRollbackException

在引擎盖下,Spring会创建两个事务拦截器:一个用于MyDao,另一个用于MyService。当daoMethod()标记回滚事务时,MyDao的拦截器执行回滚并返回。但是然后堆栈移动到MyService的拦截器,它发现了前一次回滚,并抛出了UnexpectedRollbackException

一种解决方案是从MyDao中删除@Transactional注释。但是现在这是不可行的,因为MyDao在很多地方被使用,这可能会导致错误。

另一个解决方案是不将事务设置为仅在daoMethod()中进行回滚,而是将serviceMethod()标记为还原MyCheckedException上的事务。但我不喜欢这个解决方案,因为我有很多这些“服务方法”,我必须明确标记所有这些以回滚该异常。此外,将来添加新服务方法的每个人都必须考虑到这一点,因此会产生错误的机会。

有没有办法可以将事务设置为仅从daoMethod()回滚并阻止Spring抛出UnexpectedRollbackException?例如,参数的某些组合isolationpropagation

2 个答案:

答案 0 :(得分:0)

在事务中抛出异常已经触发回滚,使用setRollbackOnly()在这里是多余的,这可能就是你有错误的原因。

如果DAO上的Transactionnal设置为默认的Propagation.REQUIRED,那么它将重用现有事务(如果已存在)或创建事务(如果没有)。在这里,它应该重用在服务层创建的事务。

答案 1 :(得分:0)

我明白了。我必须明确地告诉“外部”拦截器,我想要回滚事务。换句话说,两个拦截器都需要“通知”有关回滚的信息。这意味着要么在MyCheckedException中抓取serviceMethod(),又要将交易状态设置为仅回滚,要么将serviceMethod()标记为@Transactional(rollbackFor=MyCheckedException.class)

但正如我在OP中所提到的,我想避免这种情况,因为它容易出错。另一种方法是默认在@Transactional上进行MyCheckedException回滚。但那是completely different story