我正在使用DB2数据库并测试了以下代码:
无论methodB是否具有Propagation.REQUIRES_NEW,无论methodB是否具有异常,方法A的结果都将正确提交。
这违反了我的假设,必须使用Propagation.REQUIRES_NEW来实现这一目标。
ClassA
@Autowire
private ClassB classB;
@Transactional
methodA(){
...
try{
classB.methodB();
}catch(RuntimeException ex){
handleException(ex);
}
...
}
ClassB
@Transactional(propagation = Propagation.REQUIRES_NEW)
methodB(){...}
感谢@Kayaman,我想我现在已经知道了。
我看到的行为是因为methodB的@Transactional注释不起作用,因此methodB被视为没有任何事务注释的普通函数。
出现问题的地方是在methodA中,我通过
super.methodB()
从ClassB的子类中调用methodB,并认为它将提供事务性methodB,但该方法不起作用: @Service
@Primary
ClassC extends ClassB{
@override
methodB(){
super.methodB();
}
}
我知道,如果您从同一类的另一个非事务方法中调用事务方法,则事务注释将不起作用。
不知道
super.methodB()
也会因为相同的原因而失败(任何人都可以提供更多的解释吗?)总之,在第一段代码的示例中,当methodB具有RuntimeException时,
如果methodB具有
NO transaction
批注:A和B共享同一笔交易; methodA不会回滚如果methodB具有
REQUIRED
注释:A和B共享同一笔交易; methodA将回滚如果methodB具有
REQUIRES_NEW
注释:A和B具有独立的事务; methodA不会回滚 最佳答案
如果没有REQUIRES_NEW
(即默认的REQUIRED
或行为类似的其他一个),则ClassB.methodB()
参与与ClassA.methodA()
相同的事务。 methodB()
中的异常将标记该事务回滚。即使您捕获到异常,事务也会回滚。
使用REQUIRES_NEW
,回滚的事务将特定于methodB()
,因此当您捕获异常时,仍然存在健康的原始非回滚事务。
ClassA
@Transactional
methodA(){
try{
classB.methodB();
}catch(RuntimeException ex){
handleException(ex);
}
}
ClassB
@Transactional
methodB(){
throw new RuntimeException();
}
上面的代码将回滚整个事务。对于
propagation=TransactionPropagation.REQUIRES_NEW
,使用methodB()
不会。如果没有对
methodB()
的任何注释,则在methodA()
级别上只有一个tx边界,Spring不会意识到抛出了异常,因为它被捕获在方法的中间。这类似于将methodB()
的内容内联到methodA()
。如果该异常是非数据库异常(例如NullPointerException
),则事务将正常提交。如果该异常是数据库异常,则将基础数据库事务设置为回滚,但是Spring不会意识到这一点。然后Spring尝试提交,并抛出一个UnexpectedRollbackException
,因为数据库不允许提交tx。显式或意外地省略注释是错误的。如果打算执行db操作,则必须使用定义良好的事务上下文,并且知道您的传播。
调用
super.methodB()
会绕过Spring的常规代理机制,因此,即使有注释,也会被忽略。最后,对我来说,调用super.methodB()
似乎是一种设计气味。使用继承来减少行数通常是不好的做法,在这种情况下会导致严重的错误。关于java - 子函数中的RuntimeException不应影响父调用函数-REQUIRES_NEW是否起作用?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/60985423/