Spring @Transactional注释在使用try catch的for循环中不起作用

时间:2019-02-07 06:34:23

标签: java spring spring-boot spring-data-jpa transactional

我的问题如下。 下面给出了伪代码

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. 如果所有保存完成,则返回true响应,否则抛出异常。
  3. rollBackTestMainMethod()中,获得响应或异常后,它将在每个项目上添加错误或成功值。
  4. 它将此项添加到名为responseList的新列表中。在完成所有操作后,它会将其作为响应发送回去。

我的问题:

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

我的进口商品:

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

2 个答案:

答案 0 :(得分:7)

这是因为您正在同一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上调用方法将导致打开交易。

答案 1 :(得分:0)

标记非公共方法@Transactional既无用又具有误导性,因为Spring不会“看到”非公共方法,因此没有为其适当的调用做任何准备。 Spring也不为它所调用的方法所调用的方法做准备。

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