子函数中的 RuntimeException 不应影响父调用函数 - REQUIRES_NEW 是否起作用?
RuntimeException in child function should not affect parent calling function - does REQUIRES_NEW play a role?
我正在使用 DB2 数据库并测试了以下代码:
不管methodB是否有Propagation.REQUIRES_NEW,如果methodB有exception,methodA的结果无论如何都会正确提交。
这违背了我的假设,即必须使用 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 被当作一个没有 any 事务注解的普通函数。
错误的地方在于,在 methodA 中,我通过 super.methodB()
从 ClassB 的子 class 调用了 methodB,并认为它会提供一个事务性 methodB,但它不起作用:
@Service
@Primary
ClassC extends ClassB{
@override
methodB(){
super.methodB();
}
}
我知道如果您从另一个非事务方法调用一个事务方法,那么事务注释将不起作用 class。
不知道 super.methodB()
也会因为同样的原因而失败(有人可以给出更多解释吗?)
综上所述,在第一个代码块的例子中,当methodB出现RuntimeException时,
如果方法B有NO transaction
注解:A&B共享同一个事务;方法 A 不会回滚
如果方法B有REQUIRED
注解:A&B共享同一个事务;方法 A 将回滚
如果方法 B 有 REQUIRES_NEW
注释:A 和 B 有单独的事务;方法 A 不会回滚
没有 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();
}
以上代码将回滚整个事务。对于 methodB()
,propagation=TransactionPropagation.REQUIRES_NEW
不会。
如果 methodB()
没有 任何 注释,methodA()
级别将只有一个 tx 边界并且 Spring 不会意识到抛出异常,因为它在方法的中间被捕获。这类似于将 methodB()
的内容内联到 methodA()
。如果该异常是非数据库异常(例如NullPointerException
),事务将正常提交。如果该异常是数据库异常,则 基础数据库事务 将被设置为回滚,但 Spring 并不知道这一点。 Spring 然后尝试提交,并抛出一个 UnexpectedRollbackException
,因为数据库不允许提交 tx。
明确或无意地省略注释是错误的。如果您打算执行数据库操作,您必须使用 定义良好的 事务上下文,并且了解您的传播。
调用 super.methodB()
绕过 Spring 的正常代理机制,因此即使有注释,也会被忽略。最后,调用 super.methodB()
对我来说似乎是一种设计味道。使用继承来减少行数通常是不好的做法,在这种情况下会导致严重的错误。
我正在使用 DB2 数据库并测试了以下代码: 不管methodB是否有Propagation.REQUIRES_NEW,如果methodB有exception,methodA的结果无论如何都会正确提交。
这违背了我的假设,即必须使用 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 被当作一个没有 any 事务注解的普通函数。
错误的地方在于,在 methodA 中,我通过 super.methodB()
从 ClassB 的子 class 调用了 methodB,并认为它会提供一个事务性 methodB,但它不起作用:
@Service
@Primary
ClassC extends ClassB{
@override
methodB(){
super.methodB();
}
}
我知道如果您从另一个非事务方法调用一个事务方法,那么事务注释将不起作用 class。
不知道 super.methodB()
也会因为同样的原因而失败(有人可以给出更多解释吗?)
综上所述,在第一个代码块的例子中,当methodB出现RuntimeException时,
如果方法B有NO transaction
注解:A&B共享同一个事务;方法 A 不会回滚
如果方法B有REQUIRED
注解:A&B共享同一个事务;方法 A 将回滚
如果方法 B 有 REQUIRES_NEW
注释:A 和 B 有单独的事务;方法 A 不会回滚
没有 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();
}
以上代码将回滚整个事务。对于 methodB()
,propagation=TransactionPropagation.REQUIRES_NEW
不会。
如果 methodB()
没有 任何 注释,methodA()
级别将只有一个 tx 边界并且 Spring 不会意识到抛出异常,因为它在方法的中间被捕获。这类似于将 methodB()
的内容内联到 methodA()
。如果该异常是非数据库异常(例如NullPointerException
),事务将正常提交。如果该异常是数据库异常,则 基础数据库事务 将被设置为回滚,但 Spring 并不知道这一点。 Spring 然后尝试提交,并抛出一个 UnexpectedRollbackException
,因为数据库不允许提交 tx。
明确或无意地省略注释是错误的。如果您打算执行数据库操作,您必须使用 定义良好的 事务上下文,并且了解您的传播。
调用 super.methodB()
绕过 Spring 的正常代理机制,因此即使有注释,也会被忽略。最后,调用 super.methodB()
对我来说似乎是一种设计味道。使用继承来减少行数通常是不好的做法,在这种情况下会导致严重的错误。