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

3

我正在使用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: ERROR: duplicate key value violates unique constraint "message_metering_payload_pkey"

我该怎么做才能捕获batchUpdate异常并只获取错误行并提交好的行。

我的批量大小为500。PostgreSQL版本为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)
1个回答

2
你不会得到BatchUpdateException,因为你可能在jdbcTemplate中使用SQLErrorCodeSQLExceptionTranslator,它以一种特殊的方式处理BatchUpdateException
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()将仅包含{{link1: EXECUTE_FAILED }},尽管有些行可能已成功插入。
请参见this问题。

更改SQLExceptionTranslator是否会产生其他副作用,例如事务的行为变化或任何其他副作用,除了异常传播方式之外,我应该关注什么? - Sameer
1
它不应该影响除“SQLException”翻译策略之外的任何内容,其他所有内容都应保持不变。 - Denis Zavedeev
我发现即使是没有错误的行也会显示状态-3。有没有办法避免这种情况,只获取错误的行? - Sameer
1
似乎不可能。请参考此链接。你的批处理中有什么错误?on conflict do nothing对你来说不是一个选项吗? - Denis Zavedeev
唯一约束冲突只是一个例子。我正在批量更新500条记录,当前出现异常时,我会回滚事务,然后逐个添加记录。我的想法是,如果我能够获取已提交的记录,那么我就可以简单地忽略错误的记录并尝试重新提交好的记录。假设在500条记录中只有一条失败了,那么我只需要识别出那一条记录并提交其余的记录即可。 - Sameer
很不幸,我不知道一个简单的方法来轻松地完成它。一些建议在问题评论中 - Denis Zavedeev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接