首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >有没有一种方法可以在由于BatchUpdateException导致失败之前跟踪或获取JPA完成的批处理迭代的总数?

有没有一种方法可以在由于BatchUpdateException导致失败之前跟踪或获取JPA完成的批处理迭代的总数?
EN

Stack Overflow用户
提问于 2019-08-09 10:10:18
回答 1查看 430关注 0票数 1

我有一个使用Spring-JPA (Hibernate)持久化N个实体的需求,并且我已经设置了我的spring批量大小= M,其中M

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

代码语言:javascript
复制
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个实体,spring batch size =5

代码语言:javascript
复制
spring.jpa.properties.hibernate.jdbc.batch_size=5

13记录是导致失败的坏记录。BatchUpdateException.getUpdateCounts()返回2,这是因为它在批处理周期的第三次迭代中失败。取而代之的是,我希望获得12次成功插入的计数。有没有任何API或某种方法可以跟踪这个,而不需要外部跟踪(这会通过多次调用flush来违背我的目的)

代码语言:javascript
复制
AtomicInteger ai = new AtomicInteger(0);
entities.forEach(entity->{ entityManager.persist(entity); 
                           ai.getAndIncrement();
                           if(ai.get() % batchsize){
                               entityManager.flush();
                           });
entityManager.flush();

谢谢

EN

回答 1

Stack Overflow用户

发布于 2019-08-21 14:55:57

有一些关于使用Hibernate批量插入Oracle12的新闻。先来个不错的。

Hibernate Oracle12批插入

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

代码语言:javascript
复制
 <property name="hibernate.jdbc.batch_size" value="3"/>

识别它有点棘手,因为Hibernate日志记录与正常模式日志记录没有什么不同。可能是因为Oracle没有将值集合传递给INSERT的语法,所以您会看到单个insert语句的日志

代码语言:javascript
复制
 Hibernate: insert into AUTHOR (name, AUTHOR_ID) values (?, ?)
 Hibernate: insert into AUTHOR (name, AUTHOR_ID) values (?, ?)
 Hibernate: insert into AUTHOR (name, AUTHOR_ID) values (?, ?)

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

代码语言:javascript
复制
 PARSING IN CURSOR #347407728 ..
 insert into AUTHOR (name, AUTHOR_ID) values (:1 , :2 )
 END OF STMT 

 EXEC #347407728:....,r=3,...

请注意,不幸的是,您不能在批处理模式中使用主键的标识列

代码语言:javascript
复制
  AUTHOR_ID INT  GENERATED ALWAYS AS IDENTITY PRIMARY KEY,

使用IDENTITY将关闭批处理模式。

getUpdateCount

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

代码语言:javascript
复制
 e.getCause().getSQLException().getUpdateCounts()

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

将其整合在一起

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

batch_size =3的示例

您将看到6个记录的行

代码语言:javascript
复制
 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批,第二批失败,成功处理了两行

代码语言:javascript
复制
 BatchUpdateException - update count: [1, 1]

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

摘要

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

验证输入的

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

刷新每个批处理

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

性能不是您的第一个目标

最重要的是,虽然决定将Hibernate用于批量数据输入,但性能绝对不是您的首要目标。您选择了舒适的数据输入,并为此支付了一些性能税。

我的测试显示,在大约50秒内存储100K批处理大小为1000的简单对象所用的时间。这不是每个对象的平均.4毫秒数,但是使用直接的SQL INSERT来处理100K行所需的时间低于2秒。因此,对于单个步骤,例如时间窗口非常窄的迁移和升级,您可以从直接使用JDBC或事件SQL中获益。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57422704

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档