@Transactional 方法调用另一个没有@Transactional 注释的方法?

Posted

技术标签:

【中文标题】@Transactional 方法调用另一个没有@Transactional 注释的方法?【英文标题】:@Transactional method calling another method without @Transactional anotation? 【发布时间】:2011-09-07 12:47:48 【问题描述】:

我在 Service 类中看到了一个方法,该方法被标记为 @Transactional,但它还调用了同一类中未标记为 @Transactional 的一些其他方法。

这是否意味着对单独方法的调用导致应用程序打开到 DB 的单独连接或暂停父事务等?

一个没有任何注解的方法被另一个带有@Transactional注解的方法调用的默认行为是什么?

【问题讨论】:

【参考方案1】:

当您在事务块中调用没有@Transactional 的方法时,父事务将继续使用新方法。它将使用来自父方法的相同连接(带有@Transactional),并且在被调用方法中引起的任何异常(不带有@Transactional)都会导致事务按照事务定义中的配置回滚。

如果您在同一实例中从带有@Transactional 的方法调用带有@Transactional 注释的方法,则被调用的方法的事务行为不会对事务产生任何影响。但是如果你从另一个具有事务定义的方法调用具有事务定义的方法,并且它们在不同的实例中,那么被调用方法中的代码将遵循被调用方法中给出的事务定义。

您可以在spring transaction documentation 的声明性事务管理部分找到更多详细信息。

Spring 声明式事务模型使用 AOP 代理。所以 AOP 代理负责创建事务。仅当实例中的方法从实例外部调用时,AOP 代理才会处于活动状态。

【讨论】:

这是弹簧默认行为吗? 是的。这是默认行为。 @Tomasz 是的。但还应该提到的是,更改从另一个 @Transactional 方法调用的方法上的事务传播将没有效果。 @Tomasz,这就是我所说的will follow the transaction definitions given in the called method 的意思。但是如果调用来自同一个对象实例,则不会产生任何影响,因为调用不会通过负责事务维护的 aop 代理传播。 @Filip,这并不完全正确,如果您从不同的对象/实例调用具有 @Transactional 定义的方法,那么即使调用方法具有不同的 @Transactional 属性,被调用的方法将遵循它自己的事务定义。【参考方案2】: 这是否意味着对单独方法的调用导致应用程序打开到 DB 的单独连接或暂停父事务等?

这取决于propagation level。这里是所有可能的级别values。

例如,如果传播级别为 NESTED,当前事务将“暂停”并创建一个新事务(注意:嵌套事务的实际创建仅适用于特定事务管理器 )

一个没有任何注解的方法被另一个带有@Transactional注解的方法调用的默认行为是什么?

默认传播级别(您称之为“行为”)是REQUIRED。如果调用带有 @Transactional 注释的“内部”方法(或通过 XML 以声明方式进行事务处理),它将在 same transaction 中执行,例如“没有什么新东西”被创建。

【讨论】:

NOT_SUPPORTED 的子调用没有任何注释呢?它是继承 NOT_Supported 还是他们打开了一个新事务,因为 REQUERED 是默认设置?例如: f1.call() f2() 带有注释 NOT_SUPPORTED 用于 f1 和 non 用于 f2。【参考方案3】:

@Transactional 标记事务边界(开始/结束),但事务本身绑定到线程。一旦事务开始,它就会在方法调用之间传播,直到原始方法返回并且事务提交/回滚。

如果调用具有@Transactional 注释的另一个方法,则传播取决于该注释的传播属性。

【讨论】:

这3个答案在某种程度上相互冲突,不确定哪个更准确。 @EricWang 只是想分享一下我今天测试了这个场景,Arun P Johny (with cmets) 的答案对于 的这个场景是最准确的内部调用。【参考方案4】:

如果内部方法没有使用@Transactional注解,内部方法会影响外部方法。

如果内部方法也用@Transactional 和REQUIRES_NEW 注释,则会发生以下情况。

...
@Autowired
private TestDAO testDAO;

@Autowired
private SomeBean someBean;

@Override
@Transactional(propagation=Propagation.REQUIRED)
public void outerMethod(User user) 
  testDAO.insertUser(user);
  try
    someBean.innerMethod();
   catch(RuntimeException e)
    // handle exception
  



@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void innerMethod() 
  throw new RuntimeException("Rollback this transaction!");

内部方法使用REQUIRES_NEW 进行注释并抛出一个RuntimeException,因此它将其事务设置为回滚,但不会影响外部事务。外部事务在内部事务开始时暂停,然后在内部事务结束后恢复。它们彼此独立运行,因此外部事务可以成功提交。

【讨论】:

为了澄清初学者,我很确定 innerMethod() 需要位于与 outerMethod() 不同的 bean(也称为 Spring 管理的 java 对象)上。如果它们都在同一个bean上,我认为innerMethod实际上不会使用在其注释中声明的事务行为。相反,它将使用在 outerMethod() 声明中声明的内容。这是因为 Spring 处理 AOP 的方式,它用于 @Transactional 注释 (docs.spring.io/spring/docs/3.0.x/spring-framework-reference/…)

以上是关于@Transactional 方法调用另一个没有@Transactional 注释的方法?的主要内容,如果未能解决你的问题,请参考以下文章

Spring中同一个service中方法相互调用事务不生效问题解决方案

Spring 忽略 @Transactional 注释

玩转Spring--消失的事务@Transactional

Transactional ejb 事务陷阱

@Transactional 注意事项方法调用

Spring 为在 @Transactional 注释方法中调用的每个 JpaRepository 方法打开一个新事务