如何从Hibernate / Oracle的错误消息中确定批量插入实体时哪个实体导致了问题。
有没有办法让Hibernate或Oracle记录这些信息?
Caused by: org.hibernate.exception.ConstraintViolationException: could not execute batch
at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:129)
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:49)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:124)
at org.hibernate.engine.jdbc.batch.internal.BatchingBatch.performExecution(BatchingBatch.java:122)
at org.hibernate.engine.jdbc.batch.internal.BatchingBatch.doExecuteBatch(BatchingBatch.java:101)
at org.hibernate.engine.jdbc.batch.internal.AbstractBatchImpl.execute(AbstractBatchImpl.java:161)
at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.executeBatch(JdbcCoordinatorImpl.java:207)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:390)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:303)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:349)
at org.hibernate.event.internal.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:67)
at org.hibernate.internal.SessionImpl.autoFlushIfRequired(SessionImpl.java:1166)
at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1223)
at org.hibernate.internal.QueryImpl.list(QueryImpl.java:101)
at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:268)
... 375 more
Caused by: java.sql.BatchUpdateException: ORA-00001: unique constraint (FHIR.SYS_C0022940074) violated
at oracle.jdbc.driver.OraclePreparedStatement.executeBatch(OraclePreparedStatement.java:11190)
at oracle.jdbc.driver.OracleStatementWrapper.executeBatch(OracleStatementWrapper.java:244)
at org.jboss.jca.adapters.jdbc.WrappedStatement.executeBatch(WrappedStatement.java:1077)
at org.hibernate.engine.jdbc.batch.internal.BatchingBatch.performExecution(BatchingBatch.java:113)
... 386 more
答案 0 :(得分:3)
我非常喜欢这个问题,我甚至写过an article which answers it in great detail。
总结一下这篇文章,您需要做的是:
假设我们有以下JPA实体:
@Entity(name = "Post")
@Table(name = "post")
public class Post {
@Id
private Long id;
private String title;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
关于此实体需要注意的最重要的事情是必须手动分配实体标识符。
现在,让我们模拟一个总是最终抛出ConstraintViolationException
的批处理:
Session session = entityManager.unwrap(Session.class);
session.doWork(connection -> {
try (PreparedStatement st = connection.prepareStatement(
"INSERT INTO post (id, title) " +
"VALUES (?, ?)")) {
for (long i = 0; i < 5; i++) {
st.setLong(1, i % 2);
st.setString(2, String.format("High-Performance Java Persistence, Part %d", i));
st.addBatch();
}
st.executeBatch();
} catch (BatchUpdateException e) {
LOGGER.info("Batch has managed to process {} entries", e.getUpdateCounts().length);
}
});
因为我们使用模运算符分配标识符,所以第三个条目将无法插入,因为它将与我们保存到数据库中的第一行发生冲突。
因此,在运行上述测试用例时,这就是我们记录的内容:
c.v.b.h.h.b.BatchExceptionTest - testInsertPosts
n.t.d.l.SLF4JQueryLoggingListener - Name:DATA_SOURCE_PROXY, Time:0,
Success:False,
Type:Prepared,
Batch:True,
QuerySize:1,
BatchSize:5,
Query:[
"INSERT INTO post (id, title) VALUES (?, ?)"],
Params:[
(0, High-Performance Java Persistence, Part 0),
(1, High-Performance Java Persistence, Part 1),
(0, High-Performance Java Persistence, Part 2),
(1, High-Performance Java Persistence, Part 3),
(0, High-Performance Java Persistence, Part 4)
]
c.v.b.h.h.b.BatchExceptionTest - Batch has managed to process 2 entries
因此,要回答这个问题,您需要使用BatchUpdateException#getUpdateCounts
方法来了解您成功处理了多少项,因此下一项是导致失败的项目。
如果你想知道为什么我会生成那些精彩的日志语句,请查看这篇文章以获取more info about datasource-proxy。