【问题标题】:Spring: transaction propagationSpring:事务传播
【发布时间】:2017-11-22 16:35:55
【问题描述】:

我有以下代码:

@Transactional
public void handle() {
    for (Item item : getItems()) {
        handle(item);
    }
}

@Transactional(propagation = Propagation.NESTED)
public void handle(Item item) {
    /* logic here */
}

假设handle() 中的循环处理 10 个项目。还假设对于 3 个项目 handle(Item) 会抛出异常。

我的问题:

[1] 外层事务会在第 10 条之后提交吗?这意味着 7 个项目的必要更改将被提交,其他 3 个项目的任何中间更改都将回滚到创建时的保存点?

[2] 那么handle(Item)中的异常会被捕获而不转发给handle()吗?这是@Transactional做的吗?

[3] 另外,我想了解以下流程的行为差异是什么:

@Transactional
public void handle() {
    for (Item item : getItems()) {
        handle(item);
    }
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void handle(Item item) {
    /* logic here */
}

根据我对文档的理解,这里将启动一个新事务,并且将暂停活动事务。这与NESTED 不同,后者创建了一个保存点并且仍在使用当前事务。但我相信,REQUIRES_NEW 也将提交 7 项的更改,而其他 3 项的任何中间更改都将被遗忘。

那么真正的区别是什么(如果有的话)?

【问题讨论】:

  • 在这种情况下,您只有一个事务。它将被提交或回滚。没有外在的关系
  • @sbjavateam 需要解释一下吗?
  • 您从同一服务中的另一个事务方法调用一个事务方法。 spring 事务与代理一起工作,在你的情况下 spring 不会为第二次调用启动新事务
  • @sbjavateam 这应该适用于 AspectJ?
  • 与 AspectJ - 是的

标签: java spring postgresql transactions spring-transactions


【解决方案1】:

NESTED 启动主事务的子事务。 REQUIRES_NEW 开始单独的事务。

如果在方法退出数据保留在数据库中(由单独的事务提交)之后使用 REQUIRES_NEW 标记方法,则无论外部事务发生什么。

在嵌套的情况下,如果外部事务回滚,则更改将回滚。

查看答案here

【讨论】:

  • 我明白了。那关于例外情况? NESTEDREQUIRES_NEW @Transactional 是否会吞下异常(并执行其回滚逻辑),以防止外部或父事务也回滚?
  • 事务不吞异常。它捕获,在捕获中回滚,并重新抛出以让您处理真正的异常。因此,在您的情况下,假设第三个 handle(item); 抛出和异常。在 REQUIRES_NEW 的情况下 2 个先前的调用保留在 DB 中,在 NESTED 的情况下,2 个先前的调用将与主事务一起回滚。
  • 知道了!什么是吞下异常的干净方法?这可以通过注释来实现吗?就我而言,我想处理尽可能多的项目。所以如果第 3 项崩溃,我们应该继续第 4、5、6 项……主/父事务永远不应该回滚。
  • handle(item); 包裹到 try/catch 中,然后继续 catch。为 handle(item); 添加 REQUIRES_NEW。我觉得够了。
【解决方案2】:

根据我对文档的理解,这里将有一个新交易 已启动,活动事务将被暂停。

  1. 如果方法handle() 和handle(Item item) 在一个服务中,则只会启动一个事务- 对于第一个方法。这叫做自我调用。见:

A Spring hard fact about transaction managementWHAT IS A TRANSACTION?.

要在handle() 中为handle(Item item) 应用事务,你应该

    @Transactional
    public void handle() {
       for (Item item : getItems()) {
         applicationContext.getBean(your service).handle(item); 
       }
    }

or use spring aspectj load time weaving for self-invocation (inside one service call one method from another)

在循环内使用 Propagation.REQUIRES_NEW 调用方法对性能不利,您为每个调用创建新事务。您可以包装在一种方法中,但要处理异常 - 仅适用于与业务逻辑相关的异常!并非所有类型的异常

@Transactional
public void handle() {
        ..some logic
        //if you really need it
        applicationContext.getBean(your service).handleBatch(getItems());
    }
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void handleBatch(Collection items) {
    for (Item item : items) {
        applicationContext.getBean(your service).handle(item)
    }
}

public void handle(Item item) {
   try{ 
      //do operation
      }catch(.appropriate type..Exception ex){
         //log exception!!!
   }
}
  1. 关于回滚:spring 会在 RuntimeException 和 Error 上回滚,但不会在检查的异常上回滚。 如果您的方法抛出一些检查异常,则将提交更改。 避免提交使用

    @Transactional(rollbackFor=CheckedException.class)

【讨论】:

    猜你喜欢
    • 2014-06-22
    • 2018-01-15
    • 1970-01-01
    • 2016-02-14
    • 2011-09-20
    • 2012-05-27
    • 1970-01-01
    • 2011-02-14
    • 1970-01-01
    相关资源
    最近更新 更多