有没有办法跟踪或获取JPA在由于BatchUpdateException而失败之前完成的批量迭代总数?



我需要使用 Spring-JPA(休眠(持久化 N 个实体,并且我已经设置了我的弹簧批大小 = M,其中 M <N。>

我将把所有 N 个实体提交到存储库,它遵循以下逻辑

entities.forEach(entity->entityManager.persist(entity));
entityManager.flush();

整个操作由 @Transactional 包装。

基于 https://vladmihalcea.com/how-to-find-which-statement-failed-in-a-jdbc-batch-update,它给了我更好的结果,但挑战在于,BatchUpdateException.getUpdateCounts(( 给出了每个批处理操作中保留的总数,但不是包括失败前所有内部迭代的总计数。

例如,如果我需要保留 100 个实体,弹簧批大小 = 5

spring.jpa.properties.hibernate.jdbc.batch_size=5

13 条记录是导致失败的不良记录。BatchUpdateException.getUpdateCounts(( 返回 2,那是因为它在批处理周期的第三次迭代中失败。相反,我想获得 12 次成功插入的计数。是否有任何 API 或某种方法可以跟踪它,而无需在外部进行跟踪,(这将通过多次调用 flush 来破坏我的目的(

AtomicInteger ai = new AtomicInteger(0);
entities.forEach(entity->{ entityManager.persist(entity); 
ai.getAndIncrement();
if(ai.get() % batchsize){
entityManager.flush();
});
entityManager.flush();

谢谢

有几个关于使用 Hibernate 批量插入到 Oracle 12 的消息。先好一个。

休眠甲骨文 12 批处理插入

事实上,如果您设置了属性,Hibernate(至少在我测试的 5.4.4 版本中(支持批量插入

<property name="hibernate.jdbc.batch_size" value="3"/>

识别它有点棘手,因为休眠日志记录与正常模式日志记录没有区别。可能是由于 Oracle 没有语法将值集合传递给 INSERT,您会看到单个插入语句的日志

Hibernate: insert into AUTHOR (name, AUTHOR_ID) values (?, ?)
Hibernate: insert into AUTHOR (name, AUTHOR_ID) values (?, ?)
Hibernate: insert into AUTHOR (name, AUTHOR_ID) values (?, ?)

但是通过检查 Oracle 10046 跟踪,您可以看到每次执行 INSERT 游标都会处理行的batch_size(请参阅 EXEC 跟踪行中的参数 r=3 - 批大小设置为 3(

PARSING IN CURSOR #347407728 ..
insert into AUTHOR (name, AUTHOR_ID) values (:1 , :2 )
END OF STMT 
EXEC #347407728:....,r=3,...

请注意,很遗憾,您无法在批处理模式下将 IDENTITY 列用于主键

AUTHOR_ID INT  GENERATED ALWAYS AS IDENTITY PRIMARY KEY,

使用 IDENTITY 将关闭批处理模式

获取更新计数

第二个好消息是,如果您在批处理中遇到异常,您可以获取当前批处理的 updateCounts - 您必须取消嵌套使用此伪代码接收的PersistenceException

e.getCause().getSQLException().getUpdateCounts()

但请注意,您需要在 Oracle 12 上使用相应的 JDBC 驱动程序来查看确切的更新计数 - 在以前的版本中,您只会看到一个不特定的错误(单个负数(。

将一切整合在一起

因此,结合这两个功能,您可以 - 至少在理论上 - 识别失败的记录

示例 batch_size =3

您会看到 6 个记录的行

Hibernate: insert into AUTHOR (name, AUTHOR_ID) values (?, ?)
Hibernate: insert into AUTHOR (name, AUTHOR_ID) values (?, ?)
Hibernate: insert into AUTHOR (name, AUTHOR_ID) values (?, ?)
Hibernate: insert into AUTHOR (name, AUTHOR_ID) values (?, ?)
Hibernate: insert into AUTHOR (name, AUTHOR_ID) values (?, ?)
Hibernate: insert into AUTHOR (name, AUTHOR_ID) values (?, ?)

即有 2 批开始,第二批失败,两行成功处理

BatchUpdateException - update count: [1, 1]

这意味着 3 + 2 行正常,第 6 行失败

总结

你可能会争辩说,Hibernate人没有做功课,阅读日志不是识别问题的好方法。我没有反对这一点,我只能提供一些见解,您可能会从 Hibernate 作者那里听到(请注意,除了异常解决数据库问题之外,我与 Hibernate 没有任何关系(。

验证输入

这当然是有争议的,但在使用批处理输入时,您应该预先验证数据,这样就不会发生异常。

冲洗每批

你反对它,但实际上它没有真正的性能惩罚。每次刷新时,INSERT 游标都会关闭并重新打开,但由于 Oracle 游标兑现这没什么大不了的。

性能不是您的首要目标

最重要的是,虽然决定使用Hibernate进行批处理数据输入,但性能绝对不是您的第一个目标。您选择舒适的数据输入,并为此支付一些绩效税。

我的测试显示了在大约 50 秒内存储 100K 个批量大小为 1000 的简单对象的经过时间。每个对象的平均时间为 0.4 毫秒,但使用直接SQL INSERT处理 100K 行需要不到 2 秒的时间。因此,对于单个步骤,例如具有极窄时间窗口的迁移和升级,您可以从使用直接 JDBC 或事件 SQL 中受益。

最新更新