Java 使用自动提交为true的jdbc时回滚批处理执行

Java 使用自动提交为true的jdbc时回滚批处理执行,java,jdbc,prepared-statement,Java,Jdbc,Prepared Statement,Im使用JDBC,autocommit=true。在其中一个操作中,我使用准备好的语句进行批插入 public void executeBatchInsert(String query, List<Object[]> entityList) { try { pstmt = conn.prepareStatement(query); for(int i=0; i<entityList.size(); i++) {

Im使用JDBC,autocommit=true。在其中一个操作中,我使用准备好的语句进行批插入

public void executeBatchInsert(String query, List<Object[]> entityList)  {
        try {
            pstmt = conn.prepareStatement(query);
            for(int i=0; i<entityList.size(); i++) {
                int j=1;
                for(Object o: entityList.get(i)) {
                    pstmt.setObject(j++, formatColumnValue(o));
                }

                pstmt.addBatch();
                if((i+1)%1000 == 0) {
                    pstmt.executeBatch();
                }
            }
            pstmt.executeBatch();
        } catch (SQLException e) {
        }
    }
public void executeBatchInsert(字符串查询,列表entityList){
试一试{
pstmt=conn.preparest陈述(查询);
对于(inti=0;i1,
执行批处理时,强烈建议不要使用
autocommit=true

话虽如此,我建议使用
getUpdateCount()
并构建相应的逻辑来执行剩余的操作


最后,
commit

事实上PreparedStatement.executeBatch并没有澄清这个问题,可能在其他地方,但我确信它不是一个原子操作,因为SQL没有批处理操作,所以executeBatch在DB级别分别执行每个语句。我在MySQL上测试了它:

t1是一个空表,其中有n1 INT(11)Not Null列,autocommit=true

    ResultSet rs1 = conn.createStatement().executeQuery("select count(*) from t1");
    rs1.next();
    System.out.println(rs1.getInt(1));

    String query = "insert into t1 (n1) values(?)";
    PreparedStatement ps = conn.prepareStatement(query);
    ps.setObject(1, 1);
    ps.addBatch();
    ps.setObject(1, null);
    ps.addBatch();
    try {
        ps.executeBatch();
    } catch (Exception e) {
        System.out.println(e);
    }

    ResultSet rs2 = conn.createStatement().executeQuery("select count(*) from t1");
    rs2.next();
    System.out.println(rs2.getInt(1));
它打印

0
java.sql.BatchUpdateException: Column 'n1' cannot be null
1

也就是说,批处理中有2个插入;第一个成功,第二个失败,仍然t1得到1行

您的问题的直接答案是:否。如果发生异常,您必须手动调用
回滚
方法。在执行此操作之前,您必须
将自动提交
设置为
错误
。默认情况下,自动提交设置为
 true
。当自动提交设置为
true
时,您无法执行
回滚
,将出现
异常

稍后不要忘记将
autoCommit
设置回
true
,否则您可能会使用其他方法获得不可预期的结果

下面是一个如何实现此功能的示例。这只是一个示意图,您可能应该更加注意如何处理
连接
准备状态
异常
等等

public void insertAndRollback(Connection connection) {
    try {
        final ArrayList parameters = new ArrayList();

        // Add your parameters to the arraylist
        parameters.add("John");
        parameters.add("Lee");
        parameters.add("Mary");
        parameters.add("Peter");
        parameters.add("Lewis");
        parameters.add("Patrick");

        final String parameterizedQuery = "insert into person (name) values (?)";

        final int batchSize = 5; // Set your batch size here
        int count = 0;
        int aux = 0;

        // Get the total number of '?' in the query
        int totalQueryParameters = Utils.countCharOccurrences(parameterizedQuery, '?');
        final int auxTotalQueryParameters = totalQueryParameters;

        final PreparedStatement preparedStatement = connection.prepareStatement(parameterizedQuery);

        // Auto Commit must be set to false
        connection.setAutoCommit(false);

        for(int i = 0; i < parameters.size(); i++)
        {
            Object obj = parameters.get(i);

            aux++;
            preparedStatement.setObject(aux, obj);

            if(totalQueryParameters == i + 1) { // Because the ArrayList starts from zero.
                // First query "parsed" - > Add to batch
                preparedStatement.addBatch();
                // One query has been added to the batch. Re-adapt the cycle.
                totalQueryParameters = totalQueryParameters + auxTotalQueryParameters;
                aux = 0;
            }

            if(++count % batchSize == 0) {
                preparedStatement.executeBatch();
            }
        }

        preparedStatement.executeBatch(); // insert remaining queries
        preparedStatement.close();
        connection.setAutoCommit(true); // Make it back to default.
    } catch (SQLException ex) {
        // Do the rollback
        doRollback(connection);

        try {
            // Make it back to default.
            connection.setAutoCommit(true);
        } catch (SQLException ex1) {
            ex1.printStackTrace();
        }

        // Dont forget to close the preparedStatement and the connection
        // if you don't need the connection open any more.

        ex.printStackTrace();
    }
}


private void doRollback(Connection c) {
    try {
        c.rollback();
    } catch (SQLException ex) {
        ex.printStackTrace();
    }
}
public void insertAndRollback(连接){
试一试{
最终ArrayList参数=新ArrayList();
//将参数添加到arraylist
参数。添加(“John”);
参数。添加(“Lee”);
参数。添加(“玛丽”);
参数。添加(“彼得”);
参数。添加(“Lewis”);
参数。添加(“Patrick”);
最后一个字符串parameteredQuery=“插入到个人(姓名)值(?);
final int batchSize=5;//在此处设置批大小
整数计数=0;
int aux=0;
//获取查询中“?”的总数
int totalQueryParameters=Utils.countCharAccounts(parameterizedQuery,“?”);
最终int auxTotalQueryParameters=总QueryParameters;
final PreparedStatement PreparedStatement=connection.prepareStatement(parameterizedQuery);
//自动提交必须设置为false
connection.setAutoCommit(false);
对于(int i=0;i添加到批处理
preparedStatement.addBatch();
//已将一个查询添加到批次。请重新调整周期。
totalQueryParameters=totalQueryParameters+辅助总计QueryParameters;
aux=0;
}
如果(++计数%batchSize==0){
preparedStatement.executeBatch();
}
}
preparedStatement.executeBatch();//插入剩余查询
preparedStatement.close();
connection.setAutoCommit(true);//使其返回默认值。
}catch(SQLException-ex){
//进行回滚
doRollback(连接);
试一试{
//将其恢复为默认值。
connection.setAutoCommit(true);
}捕获(SQLException ex1){
ex1.printStackTrace();
}
//不要忘记关闭preparedStatement和连接
//如果您不再需要连接,请打开。
例如printStackTrace();
}
}
专用void doRollback(连接c){
试一试{
c、 回滚();
}catch(SQLException-ex){
例如printStackTrace();
}
}

使用
autoCommit=true执行批处理的行为(明确!)未在JDBC规范中定义。通常,如果不禁用
autoCommit
,就不应该使用批处理执行。有趣的是。因此,假设有两个线程t1和t2更新表中的记录。如果t1更新1-100条记录,t2更新50-150条记录。当两个线程都尝试执行“executeBatch”时,t2将在t1上被阻止,因为t1锁定了这些记录。如果t2提交失败(出于某种原因),记录(部分)会被提交并且锁会在50-150上被释放吗?或者我们应该显式地对其执行回滚吗?但是您说的autocommit=true。主要的一点是executeBatch与2(或更多)的行为insert/update语句与执行两个单独的executeUpdate语句相同。明白了。所以,在这种情况下不会有任何回滚。如果批处理有1-10,如果它在5处出现异常,那么将提交1-4,而不会执行5-10。Rite?当然,但我建议模拟和测试任何您怀疑的情况谢谢但是,我认为您的catch子句中可能有一个错误:
//使其返回默认值.connection.setAutoCommit(false);
我相信您也希望在此处将其设置为
true