如何在 postgreSql 中使用 JDBCTemplate batchUpdate 捕获错误记录?

问题描述

我正在使用 Spring JDBCTemplate 和 BatchPreparedStatementSetter 在 postgresql 数据库上执行批量更新。我想捕获错误的记录,经过一些帖子后,发现捕获 BatchUpdateException 然后查找“Statement.EXECUTE_Failed”可以帮助我识别错误的记录。但是,当我按如下方式实现它时,我永远不会收到 batchUpdate 异常。

在这里,我试图重复输入相同的 ID“120”,以便我得到一个唯一的约束违规,以在数据库级别强制异常。

@Override
@Transactional
public void batchInsertTenantPayloads(List<Payload> payloadMessages) {

    try {
        jdbcTemplate.batchUpdate(DBQueryConstants.INSERT_INTO_MESSAGE_METERING_PAYLOAD,new BatchPreparedStatementSetter() {

            @Override
            public int getBatchSize() {
                return payloadMessages.size();
            }

            @Override
            public void setValues(PreparedStatement ps,int i) throws sqlException {
              
                ps.setLong(1,120);
                ps.setString(2,payloadMessages.get(i).getId());
                ps.setDate(3,Date.valueOf(payloadMessages.get(i).getTs().toLocalDate()));
                ps.setString(4,payloadMessages.get(i).getContent().getId());
                ps.setInt(5,payloadMessages.get(i).getStatus().ordinal());
                ps.setString(6,MyUtils.toJSON(payloadMessages.get(i)));
            }
        });

    } catch (Exception e) {
        if (e.getCause() instanceof BatchUpdateException) {
            LOGGER.info("Batch exception occurred");
            BatchUpdateException be = (BatchUpdateException) e.getCause();
            int[] batchUpdateCounts = be.getUpdateCounts();
            for (int index = 0; index < batchUpdateCounts.length; index++) {
                if (batchUpdateCounts[index] == Statement.EXECUTE_Failed) {
                    LOGGER.error(
                            "[MMC] batch update Error execution >>>>>>>>>>>" + index + " ---,codeFail : " + batchUpdateCounts[index]
                                    + "---,line " + payloadMessages.get(index));
                }
            }

        }

    }

}

挑战在于我从不进入 BatchUpdateException 块。 相反,我得到一个

org.springframework.dao.DuplicateKeyException

并且嵌套异常是

org.postgresql.util.PsqlException:错误:重复的键值违反 唯一约束“message_metering_payload_pkey”

我可以做些什么不同的事情,以便我可以捕获 batchUpdate 异常并仅获取错误行并提交好的行。

我的批量大小是 500。Postgre 版本是 9.6,我使用的是 spring 2.0 和 spring jdbc 5.1.3。

堆栈跟踪看起来像这样

org.springframework.dao.DuplicateKeyException: PreparedStatementCallback; sql [insert into message_metering_payload(id,tenant_name,source_dt,exchange_id,status,payload)values(?,?,?)]; ERROR: duplicate key value violates unique constraint "message_metering_payload_pkey"
  Detail: Key (id)=(120) already exists.; nested exception is org.postgresql.util.PsqlException: ERROR: duplicate key value violates unique constraint "message_metering_payload_pkey"
  Detail: Key (id)=(120) already exists.
    at org.springframework.jdbc.support.sqlErrorCodesqlExceptionTranslator.doTranslate(sqlErrorCodesqlExceptionTranslator.java:242)
    at org.springframework.jdbc.support.AbstractFallbacksqlExceptionTranslator.translate(AbstractFallbacksqlExceptionTranslator.java:72)
    at org.springframework.jdbc.core.JdbcTemplate.translateException(JdbcTemplate.java:1444)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:632)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:646)
    at org.springframework.jdbc.core.JdbcTemplate.batchUpdate(JdbcTemplate.java:936)

解决方法

您没有得到 BatchUpdateException,因为您可能会在处理 jdbcTemplateSQLErrorCodeSQLExceptionTranslatorBatchUpdateException 中使用 in a special way

if (sqlEx instanceof BatchUpdateException && sqlEx.getNextException() != null) {
    SQLException nestedSqlEx = sqlEx.getNextException();
    if (nestedSqlEx.getErrorCode() > 0 || nestedSqlEx.getSQLState() != null) {
        sqlEx = nestedSqlEx;
    }
}

有一个问题:

如果您使用 SQLStateSQLExceptionTranslator,您可以缓解这种情况:

jdbcTemplate.setExceptionTranslator(new SQLStateSQLExceptionTranslator());

然后您将获得 BatchUpdateException 作为 cause

try {
    // ...
} catch (DataAccessException e) {
    Throwable cause = e.getCause();
    logger.info("cause instanceof BatchUpdateException = {}",cause instanceof BatchUpdateException);
 }

但是请注意,在 postgresql jdbc 驱动程序的情况下,BatchUpdateException#getUpdateCounts() 将仅包含 EXECUTE_FAILED,尽管可以成功插入某些行。

参见 this 问题