【问题标题】:Spring @Transactional anotation not working in a for loop with using try catchSpring @Transactional 注释在使用 try catch 的 for 循环中不起作用
【发布时间】:2019-07-01 05:30:30
【问题描述】:

我的问题如下。 伪代码如下

public Object rollBackTestMainMethod(List<Object> list) {

  List<Object> responseList = new ArrayList<>();

  for(Object item:list){

    try{    
      Boolean isOperationSuccess = rollBackTestSubMethod(item);
      if (isOperationSuccess==null || !isOperationSuccess){
        item.addError("Operation failed");
        item.addSuccess(false);
      } else {
        item.addError(null);
        item.addSuccess(true);
      }

    } catch(Exception exception) {
      item.addError(exception.getMessage());
      item.addSuccess(false);
    }

    responseList.add(item);
  }

  return responseList;
}

@Transactional(rollbackFor = {Exception.class, SQLException.class})
private Boolean rollBackTestSubMethod(Object listItem){

  Long value1=save(listItem.getValue1());
  if(value1==null){
    throw new Exception("Error during save 1");
  }

  Long value2=save(listItem.getValue2());
  if(value2==null){
    throw new Exception("Error during save 2");
  }
  Long value3=save(listItem.getValue3());
  if(value3==null){
    throw new Exception("Error during save 3");
  }

  return Boolean.TRUE;
}

我在这里做什么:

  1. rollBackTestMainMethod() 中迭代一个列表。在rollBackTestSubMethod() 中发送一个列表项并执行 3 次保存操作。
  2. 如果全部保存完成则返回真响应,否则抛出异常。
  3. rollBackTestMainMethod()中,得到响应或异常后,在每一项上添加错误或成功值。
  4. 正在将此项添加到名为responseList 的新列表中。在所有操作之后,它会将其作为响应发送回来。

我的问题:

  1. rollBackTestSubMethod() 抛出后,它不会被回滚,因为它是从 try catch 块调用的。
  2. 如果我想通过TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 强制回滚,那么对于任何抛出/异常,它将回滚所有项目。
  3. 这里我只想回滚投掷物品而不是所有物品。
  4. 此方法在 Spring bean 中
  5. 我正在通过 spring data jpa 将数据保存到我的关系数据库中

我的进口:

import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

【问题讨论】:

  • rollbackFor = {Exception.class, SQLException.class} 是无关的。方法没有throws SQLException 或任何其他检查异常,@Transactional 默认回滚RuntimeException
  • 感谢先生的评论。我只是添加了 sudo 代码。请忽略 SqlException :)
  • “须藤代码”?你的意思是pseudocode
  • 哦,对不起,它的伪代码
  • Sudo 代码其实比伪代码好。支持自己@Andreas

标签: java spring hibernate spring-boot spring-data


【解决方案1】:

这是因为您在同一个 bean 中调用 @Transactional 方法。

@Transactional 仅适用于在 spring 创建的代理上调用的方法。这意味着,当您创建 @Service 或其他 bean 时,从外部调用的方法将是事务性的。如果从 bean 内部调用,则不会发生任何事情,因为它不通过代理对象。

最简单的解决方案是将方法移动到另一个 @Service 或 bean。如果你真的想把它保存在同一个组件中,那么你需要调用它,以便它被spring AOP包裹在代理中。你可以这样做:

private YourClass self;

@Autowired
private ApplicationContext applicationContext;

@PostConstruct
public void postContruct(){
    self = applicationContext.getBean(YourClass.class);
}

然后在self 上调用方法将导致打开事务。

【讨论】:

  • 感谢您的快速回答。我正在通过 spring data JPA 将数据保存到我的数据库中。您能否详细说明您的答案。另外我怎样才能实现我的目标,“我只想回滚投掷的物品而不是所有物品”
  • 我的解释中有什么不清楚的地方?:) 这是事务的概念 - 它作为一个原子操作回滚......
  • 另外,如果您发现此答案有帮助,请考虑支持/标记为正确以帮助其他有同样问题的人:)
  • 我真的很想把这些东西放在一个服务中,但对我来说,self 提议似乎应该被省略。这是一个很好的方法还是只是最后的手段?
  • @devaga 你说得对,将这些东西放在单独的服务中比自我注入更干净。但这是可能的,而且这个技巧在极少数情况下会派上用场
【解决方案2】:

标记非公共方法@Transactional 既无用又具有误导性,因为Spring 没有“看到”非公共方法,因此没有为它们的正确调用做准备。 Spring 也没有为它调用的方法调用的方法做准备。

因此标记一个私有方法,例如@Transactional只有在方法实际写成@Transactional时才会导致运行时错误或异常。

【讨论】:

    猜你喜欢
    • 2014-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-07
    • 1970-01-01
    • 2020-06-04
    • 2020-12-11
    相关资源
    最近更新 更多