如何在 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
只会回滚 RuntimeException
和 Error
但不会回滚已检查的异常。这意味着如果您还想回滚已检查的异常,则必须捕获所有已检查的异常并将其作为 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()
清除会话中的内存,这样如果你插入一个很多记录。
为了简化我的场景,我将给出以下示例:
我正在使用 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
只会回滚 RuntimeException
和 Error
但不会回滚已检查的异常。这意味着如果您还想回滚已检查的异常,则必须捕获所有已检查的异常并将其作为 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()
清除会话中的内存,这样如果你插入一个很多记录。