JDBC PreparedStatement 批处理在错误时继续插入

9 浏览
0 Comments

JDBC PreparedStatement 批处理在错误时继续插入

大家好,我在Java中使用PreparedStatement创建了一个批处理,代码如下:

for(Item  item: list){
    ps.setString(1, item.getSome());
    ps.setString(2, item.getFoo());
    ps.setString(3, item.getBatman());
    statement.addBatch();
    if (++count % batchSize == 0) {
        results = ps.executeBatch(); //执行部分批处理
        if (results != null)
           System.out.println(results.length);
    }
}
results= ps.executeBatch(); //执行剩余的批处理

数据库服务器是MySQL,我在插入表时有一些限制条件,由于这些限制条件,当我插入数据时会产生错误。我想要运行批处理并忽略错误,但目前会抛出异常并终止批处理。

之前,我通过一个大的循环逐个保存数据,如下:

//伪代码
for item
try{
   insert item
}catch(E){什么都不做}

但是这种方法非常慢,在某些情况下,批处理处理了4000个数据,插入了1500个,忽略了剩下的。

我该如何处理批处理呢?

编辑:

我使用weblogic连接到MySQL数据库,使用的驱动程序是`mysql-connector-java-commercial-5.0.3-bin`。

我尝试了以下属性:

1. `continueBatchOnError=true`

2. `rewriteBatchedStatements=true`

3. `continueBatchOnError=true`和`rewriteBatchedStatements=true`

并且使用了`connection.setAutoCommit(false)`,但仍然会在重复数据时抛出异常。

编辑:

忘记提到,我在连接方面使用了Hibernate + Spring。

唯一的逐个保存示例是在Hibernate中进行的,但出于性能考虑,我尝试使用JDBC批处理,在Web应用程序的其他过程中也使用了Hibernate的连接,并且运行良好。

这是完整的代码:

@Transactional
public void saveMany(final List items) {
    getMySqlSession().doWork(new Work() {
        @Override
        public void execute(Connection connection) throws SQLException {
            StringBuilder sb = new StringBuilder();
            sb.append("INSERT INTO `FRT_DB`.`ITEM` ");
            sb.append("( ");
            sb.append("`masterID`, ");
            sb.append("`agent`, ");
            sb.append("`rangeID`) ");
            sb.append("VALUES ");
            sb.append("( ");
            sb.append("?, ");
            sb.append("?, ");
            sb.append("?) ");
            int[] results = null;
            PreparedStatement ps = null;
            try {
                connection.setAutoCommit(false);
                ps = connection.prepareStatement(sb.toString());
                final int batchSize = 250;
                int count = 0;
                for (Item item : items) {
                    if (item.getMasterId() != null) {
                        ps.setInt(1, item.getMasterId());
                    } else
                        ps.setNull(1, java.sql.Types.INTEGER);
                    if (item.getAgent() != null) {
                        ps.setString(2, item.getAgent());
                    } else
                        ps.setNull(2, Types.VARCHAR);
                    if (item.getRangeId() != null)
                        ps.setInt(3, item.getRangeId());
                    else
                        ps.setNull(3, Types.INTEGER);
                    ps.addBatch();
                    if (++count % batchSize == 0) {
                        results = ps.executeBatch();
                        if (results != null)
                            System.out.println(results.length);
                    }
                }
                results= ps.executeBatch();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
}

这会产生以下异常:

java.sql.BatchUpdateException: Duplicate entry '1-000002725' for key 'masterID'

但是我需要继续执行。Spring + Hibernate的设置是否会干扰JDBC的属性?我不知道。

0
0 Comments

JDBC PreparedStatement Batch continue insert on error的问题出现的原因是在运行批处理时使用了INSERT IGNORE语句。使用INSERT IGNORE语句时,不会抛出与约束相关的异常,而是会跳过错误并保留旧数据。如果需要保存新数据,则需要改为使用INSERT ... ON DUPLICATE KEY语句。在代码中不需要使用Commit或Rollback。在try块中,只会在SqlException而不是BatchUpdateException中崩溃。需要添加allowMultiQueries属性,并将其设置为true。在使用mssql时,没有使用ignore选项,但在jdbcTemplate中可能有其他选项。

解决这个问题的方法是将INSERT IGNORE语句改为INSERT ... ON DUPLICATE KEY语句,并添加allowMultiQueries属性设置为true。对于使用mssql的情况,可能需要查看jdbcTemplate中的其他选项。可以参考http://dev.mysql.com/doc/connector-j/en/connector-j-reference-configuration-properties.html获取更多信息。

0