Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 嵌套@Transactional方法中的Spring意外回滚异常_Java_Spring_Hibernate_Transactions - Fatal编程技术网

Java 嵌套@Transactional方法中的Spring意外回滚异常

Java 嵌套@Transactional方法中的Spring意外回滚异常,java,spring,hibernate,transactions,Java,Spring,Hibernate,Transactions,我有一个dao类(MyDao),它用@Transactional注释(在类级别上)标记,没有附加参数。在这个dao类中,我有一个方法,在某些情况下需要抛出一个已检查的异常并执行事务回滚。大概是这样的: @Transactional public class MyDao { public void daoMethod() throws MyCheckedException() { if (somethingWrong) { Transaction

我有一个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();
    }

}
问题是,当从
serviceMethod()
调用
daoMethod()
并将事务标记为仅回滚时,我会得到一个
意外回滚异常

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

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

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


有没有一种方法可以让我只从
daommethod()
将事务设置为回滚,并防止Spring抛出
意外回滚异常
?例如,由于一些参数的组合
隔离
传播

在事务内部引发异常已经触发回滚,因此使用setRollbackOnly()在这里是多余的,这可能就是您出现该错误的原因


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

我找到了答案。我还必须明确地告诉“外部”拦截器,我要回滚事务。换句话说,两个拦截器都需要“通知”回滚。这意味着在
serviceMethod()
中捕获
MyCheckedException
,并将事务状态设置为仅回滚,或者像这样标记
serviceMethod()
@Transactional(rollboor=MyCheckedException.class)


但正如我在OP中提到的,我想避免这种情况,因为它容易出错。另一种方法是在默认情况下使
@Transactional
回滚到
MyCheckedException
。但默认情况下,这是一个仅限Spring在运行时异常时回滚的事务。这可以通过@Transactional注释上的属性
rollboor
进行修改。但是当我在MyDao上执行此操作时,得到的结果与设置事务状态相同。您应该使用框架配置回滚检查的异常,而不是手动更改事务状态,您可以将rollbackor on exception.class设置为回滚所有检查的异常。您可以将其放在所有服务上(或者使用该配置创建一个可替代的注释)。您还可以将选中的异常放在运行时异常中,并将其抛出。我不知道您的用例,但它可能是一个解决方案。我的问题不是“如何回滚事务”。我的问题是,当我有两个“嵌套”拦截器时,内部拦截器执行回滚,然后外部拦截器抛出UnexpectedRollbackException。我告诉Spring执行回滚的方式并不重要。