何时使用Spring的@Transactional(propagation = Propagation.SUPPORTS)?

30
根据Spring javadoc @Transactional(propagation = Propagation.SUPPORTS)

如果当前不存在事务,则支持一个当前事务执行非事务性操作。类似于同名的EJB事务属性。

看起来我只需声明方法为非事务性即可完成操作,所以我的问题是:
  • 在哪些情况下需要使用SUPPORTS传播?
  • SUPPORTS传播的意义何在?
能否给出SUPPORTS传播实际有用的真实示例/场景?

请看这个答案:https://dev59.com/S2w15IYBdhLWcg3wuuGn - Grigory Kislin
4个回答

8
我能为您翻译以下IT技术相关内容:

我能想到的最简单的例子是,一个方法将内容发送到JMS服务器。如果你在事务的上下文中,你希望消息与事务范围相耦合。但是如果没有正在运行的事务,为什么要打扰调用事务服务器并启动一个只做一次信息的事务呢?

请记住这些可以在API以及实现中声明。因此,即使对于您的用例之间放置它和放置没有什么区别,对于API作者指定哪些操作可以包括在事务中,而不是可能调用不参与事务的外部系统的操作,仍然增加了价值。

当然,这是在JTA上下文中。在仅限于资源本地物理数据库事务的系统中,此功能实际上没有多少实际用途。


在你提供的JMS示例中,如果没有tx但是jms会话/生产者是共享的,那么发送作为TX一部分的消息。我想分布式TX是SUPPORTS有用的唯一场合。 - ams
如果您有NOT_SUPPORTED的实例并且想要区分,我认为它作为API标记在非分布式设置中仍然可以很有用。但是,如果tx未分布,从实现上讲,它与根本不放置任何内容没有太大区别,您是正确的。 - Affe
1
我不明白为什么分布式与否会有任何区别。一个没有SUPPORTS但没有添加NOT_SUPPORTED的方法,确实支持事务。这就是所有这些的重点。这就像在每个非最终方法中添加“@PossiblyOverridden”注释一样。毫无意义!Affe,你的第一段话...是错误的吗?“如果没有事务正在运行”,那么就没有事务正在运行。你的第二段话也是错的吗?同样,如果我不支持事务,我使用NOT_SUPPORTED。任何没有事务注释的地方都有效地“支持”。 - Martin Andersson
1
我不确定你想要什么样的回应,评论一个关于18年前API的7年前答案,似乎基于现代感性而言是一个愚蠢的设计... 是的...它实际上“什么也没做”...是的,20年前Sun工程师与今天的Google和Facebook工程师有不同的设计哲学?如果我今天回答这个问题,我可能会写一些像“那就是我们在2002年的方式,那些对UML感到兴奋的人像那样重视‘完整性’,尽管设置实际上是一个无操作。”的东西。 - Affe

5

在使用ORM的情况下,readOnly=true事务标志与select操作配合使用效果更佳:

@Transactional(readOnly = true, propagation=Propagation.SUPPORTS)
public Pojo findPojo(long pojoId) throws Exception {
   return em.find(Pojo.class, pojoId);
}

在这种情况下,您需要确保在执行选择操作时不要为创建新事务支付价格(如果没有已经存在的事务)。
尽管如果您已经进行了这样的思考过程,您甚至可能考虑完全放弃事务方面的考虑:
public Pojo findPojo(long pojoId) throws Exception {
   return em.find(Pojo.class, pojoId);
}

那么...在通过ORM框架(例如MyBatis)从本地数据库进行选择操作时,真的值得使用@Transactional注释吗? - aloplop85
“放弃事务性方面”是指使用@NotTransactional吗? - John Little
如果某个东西没有事务方面,或者是@NotTransactional,并且它在事务内部被调用,会发生什么?例如,如果事务性方法A将某些内容写入数据库,然后调用NotTransactional方法B,该方法也写入一些内容,那么无论是A还是B抛出未经检查的异常,都会被回滚。 - John Little
我仍然看不出有SUPPORTS和没有它有什么区别。我认为API设计者只是过分关注注释。也许将来的某一天,我们会扭转这种恶劣现象,回归上帝所期望的真正面向对象编程方式。 - Martin Andersson

0

使用 Propagation.SUPPORTS 可以在抛出异常时触发设置回滚标志。例如,在以下代码中,尽管有 catch 块,但当离开 txRequired 块时将抛出 UnexpectedRollbackException

TransactionTemplate txRequired = new TransactionTemplate(platformTransactionManager);
TransactionTemplate txSupports = new TransactionTemplate(platformTransactionManager);
txRequired.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
txSupports.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS);

//prints 'Exception handled' and then throws UnexpectedRollbackException
txRequired.executeWithoutResult(tx -> {
    try {
        txSupports.executeWithoutResult(tx2 -> {
            throw new RuntimeException();
        });
    } catch (Exception ex) {
        System.out.println("Exception handled");
    }
}); 

以下代码显然不会抛出任何异常。唯一的区别是缺少PROPAGATION_SUPPORTS块。

txRequired.executeWithoutResult((tx) -> {
    try {
        throw new RuntimeException();
    } catch (Exception ex) {
        System.out.println("Exception handled");
    }
});

0
根据这个问题使用Propagation.SUPPORTS来提高只读操作的性能,您不应该使用Propagation.SUPPORTS设置只读事务:
这种变化是否真正提高了性能还不清楚。其中有多个方面需要考虑。首先,链接的文章已经过时,并且存在很大缺陷,因为它过于简化了事情。如果您想要详细了解,我可以进一步解释,但现在就到此为止吧。在执行性能方面有很多因素需要考虑。如果没有正在进行的事务,则readOnly标志不会传播到JDBC驱动程序(这将导致许多数据库的优化无法应用),也不会应用Spring的JPA资源管理代码中的优化,例如显式关闭刷新,如果应用,则可以显著提高在读取大量数据时的性能。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接