使用Hibernate和Oracle的批量插入不会记录哪个实体导致ConstraintViolationException

时间:2017-03-20 09:15:03

标签: java oracle hibernate

如何从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

1 个答案:

答案 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方法来了解您成功处理了多少项,因此下一项是导致失败的项目。

P.S。

如果你想知道为什么我会生成那些精彩的日志语句,请查看这篇文章以获取more info about datasource-proxy