【问题标题】:External transaction rollback when internal transaction(REQUIRES_NEW) throw exception内部事务(REQUIRES_NEW)抛出异常时的外部事务回滚
【发布时间】:2019-01-21 07:39:34
【问题描述】:

我有方法:

  @Transactional
  public void importChargesRequest() {
  ...
   for (Charge charge : charges) {

      try {
        Charge savedCharge = saveCharge(charge);
      } catch (Exception e) {
        log.error(e.getMessage());
      }
    }
}

对于坚持每个Charge我调用内部方法:

@Transactional(propagation = Propagation.REQUIRES_NEW)
  public Charge saveCharge(Charge charge) {
    return chargesRepository.saveAndFlush(charge);
  }

如果saveCharge 方法抛出异常(在我的情况下是约束异常)我想写日志,并继续保留另一个实体。但是当我捕获异常时 - 我的外部事务回滚错误:

Transaction was marked for rollback only; cannot commit; nested exception is org.hibernate.TransactionException: Transaction was marked for rollback only; cannot commit

我需要打开事务并开始保存每个实体。如果某些实体无法保存异常 - 我需要记录此异常并继续保存另一个实体。当所有实体都将保存(或记录)时,我需要提交外部事务。但现在它回滚了。我该如何解决?

编辑:

我接受了 cmets 并将 REQUIRES_NEW 事务转移到另一个 bean:

@Service
public class TestService {

  private final TestDao testDao;

  public TestService(TestDao testDao) {
    this.testDao = testDao;
  }

  @Transactional
  public void saveTest() {
    for (int i = 0; i < 100; i++) {
      Test test = new Test();
      if (i == 10 || i == 20) {
        test.setName("123");
      } else {
        test.setName(UUID.randomUUID().toString());
      }
      testDao.save(test);
    }
  }
} 

每个内部事务的另一个 bean:

@Slf4j
@Component
@Repository
public class TestDao {

  @PersistenceContext
  private EntityManager entityManager;

  @Transactional(propagation = Propagation.REQUIRES_NEW)
  public void save(Test test) {
    entityManager.persist(test);
  }
}

当我第一次尝试保存时,数据库中有 20 行。每次下一次保存我都会得到 +10 行。名称有约束。当我收到错误时-事务已提交且不继续。每次保存后我等待 98 行。

【问题讨论】:

  • 我不确定我是否理解您在编辑/附录中所说的内容。 “每次保存后我都会等待 98 行。” - 你的意思是说你在每次保存后期望 98 新行(除了第一行,它应该给出99 新行,因为第一个“123”情况是正确的)?如果您在第一次调用 saveTest 后看到 20 行新行,并且在每次下一次调用 saveTest 后又看到 10 行,这表明 TestDao.save(...) 方法实际上不是事务性的。您可以通过设置断点/记录异常并查看堆栈跟踪来测试吗?

标签: java spring spring-data-jpa spring-transactions


【解决方案1】:

您应该将方法saveCharge 移动到其他类 因为 spring 事务使用代理模式,这需要其他类。

创建新的服务类并注入主类

例如:

@Service
public class A{

@Autowire
private B b;

@Transactional
  public void importChargesRequest() {
  ...
   for (Charge charge : charges) {

      try {
        Charge savedCharge = b.saveCharge(charge);
      } catch (Exception e) {
        log.error(e.getMessage());
      }
    }
}
}

@Service
public class B{

@Transactional(propagation = Propagation.REQUIRES_NEW)
  public Charge saveCharge(Charge charge) {
    return chargesRepository.saveAndFlush(charge);
  }
}

【讨论】:

    【解决方案2】:

    如果 saveCharge 是与 importChargesRequest 相同的 bean 中的方法,则 @Transactional 注释将被忽略,并且 saveAndFlush 在同一(外部)事务中工作。 (我确信当您使用代理/拦截器来管理事务时就是这种情况。我相当肯定在使用基于 aspectj 的事务拦截时也是这种情况)。

    通常,如果异常一直冒泡到外部方法(标记为@Transaction 的方法),则事务只会被标记为回滚,但我怀疑存储库或事务管理器本身(休眠会话)直接将事务标记为由于违反约束而回滚。

    解决方案是将 saveCharge 移动到另一个 bean 并使用 importChargesRequest 方法将该 bean 注入到 bean 中。

    @Service
    public class ChargesDataService{
    
      @Autowire
      private ChargesRepository chargesRepository;
    
      @Transactional(propagation = Propagation.REQUIRES_NEW)
      public Charge saveCharge(Charge charge) {
        return chargesRepository.saveAndFlush(charge);
      }
    }
    
    @RestController
    public class ChargesController{
    
      @Autowire
      private ChargesDataService chargesDataService;
    
      @Transactional
      public void importChargesRequest() {
    
       for (Charge charge : charges) {
    
          try {
            Charge savedCharge = chargesDataService.saveCharge(charge);
          } catch (Exception e) {
            log.error(e.getMessage());
          }
        }
      }
    }
    

    附录: 注释被忽略,因为您不再通过代理到 bean-instance,这意味着没有拦截器被调用,这意味着没有地方可以处理新事务。 您可以通过在saveCharge 方法中设置断点并查看堆栈跟踪^ 来检查是否是这种情况。寻找类似事务拦截器 invokeWithinTransaction 的方法。

    ^ 您还可以创建一个新异常,调用 fillInStacktrace,然后记录/打印异常,包括其堆栈跟踪。

    【讨论】:

      【解决方案3】:

      你正在做saveCharge(charge),即this.saveCharge(charge)(你正在以这种方式绕过spring代理)-spring无法为此创建一个new事务-你需要将该方法放在一个单独的Bean

      【讨论】:

      • 我编辑了我的问题。我遇到了新问题
      猜你喜欢
      • 2017-11-21
      • 1970-01-01
      • 1970-01-01
      • 2014-07-17
      • 1970-01-01
      • 2015-10-03
      • 2020-09-24
      • 2021-10-15
      • 2012-10-14
      相关资源
      最近更新 更多