我有一个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
?例如,参数的某些组合isolation
和propagation
?
答案 0 :(得分:0)
在事务中抛出异常已经触发回滚,使用setRollbackOnly()在这里是多余的,这可能就是你有错误的原因。
如果DAO上的Transactionnal设置为默认的Propagation.REQUIRED,那么它将重用现有事务(如果已存在)或创建事务(如果没有)。在这里,它应该重用在服务层创建的事务。
答案 1 :(得分:0)
我明白了。我必须明确地告诉“外部”拦截器,我想要回滚事务。换句话说,两个拦截器都需要“通知”有关回滚的信息。这意味着要么在MyCheckedException
中抓取serviceMethod()
,又要将交易状态设置为仅回滚,要么将serviceMethod()
标记为@Transactional(rollbackFor=MyCheckedException.class)
。
但正如我在OP中所提到的,我想避免这种情况,因为它容易出错。另一种方法是默认在@Transactional
上进行MyCheckedException
回滚。但那是completely different story。