JDBC PreparedStatement 批处理在错误时继续插入
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的属性?我不知道。
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获取更多信息。