如何在 Spring Boot 应用程序的 @Transactional 方法中使用纯 Hibernate 保证原子批量插入

How to guarantee atomic batch insert with pure Hibernate inside @Transactional method in Spring Boot application

为了简化我的场景,我将给出以下示例:

我正在使用 Spring 启动应用程序 spring.jpa.properties.hibernate.order_inserts=true 属性。

在标有@Transactional 注释的java 方法中,我想使用纯休眠执行几千条记录的批量插入。批量插入由一个单独的方法执行,该方法由带有@Transactional 的主方法调用。我是这样做的:

@Transactional
public void doSomeStuff() {
......
insertRecords(records, session);
......
}


private void insertRecords(final List<Record> records, final Session session) {
  int counter = 0;
  int batchSize = 50;
  for(Record r: records){
    session.save(r);

    counter ++;

    if(counter > 0 && counter % batchSize == 0){
       session.flush(); 
       session.clear();
    }

    }
}     

问题是我希望操作是原子操作,如果其中一个批插入操作失败,则所有操作都无法回滚。我知道 @Transactional 注释将保证自动回滚 RunTimeException 或错误。这是否意味着如果其中一个批次失败,@Transactional 将覆盖回滚,或者我应该用 try/catch 块围绕 for 循环并抛出一个已检查的异常并使用 @Transactional 的 rollbackFor 属性?我不确定是否选中或未选中批处理失败期间抛出的异常。

另一个重要的问题是,在这种情况下,hibernate 是否会在成功插入的情况下自动提交每个批次?这意味着如果一批失败,我将无法回滚已经提交的一次,这会破坏原子性。

此外,我不想手动管理提交,而是想将其处理到 Spring 提供的事务上下文。

你是对的。默认情况下,@Transactional 只会回滚 RuntimeExceptionError 但不会回滚已检查的异常。这意味着如果您还想回滚已检查的异常,则必须捕获所有已检查的异常并将其作为 RuntimeException 重新抛出,或者只需使用 @Transactional.[= 中的 rollbackFor 设置25=]

I'm not sure if the exceptions thrown during batch failure are checked or unchecked.

java编译器会帮助检查it.If一些内部方法抛出检查异常,它要求你必须处理它,你既不能捕获它也不能指定抛出这个检查异常方法声明。如果不这样做,代码将无法编译。

这意味着如果您的代码可以编译,则不会从内部方法中抛出已检查的异常,您可以简单地坚持当前设置。另一方面,如果从内部方法抛出一些已检查的异常,您可以选择捕获它并重新抛出它作为 RuntimeException :

@Transactional
public void doSomeStuff()  {
    try{

    }catch(Exception ex){
      throw new RuntimeException("something goes wrong.." ,ex);
    }
}

或配置rollbackFor

@Transactional(rollbackFor={Exception.class})
public void doSomeStuff() throws Exception{

}

此外,请注意 session.flush() 与提交事务不同,因此您的代码不会为每批提交。相反,事务将在标有 @Transactional return 的方法成功提交后提交,在您的情况下,当插入所有记录时,这是 doSomeStuff() 的 returned。

flush()的重点是为了后续的session.clear()清除会话中的内存,这样如果你插入一个很多记录。