使用带有autocommit = true的jdbc时回滚批处理执行

时间:2013-01-31 12:04:30

标签: java jdbc prepared-statement

我使用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) {
        }
    }

如果我在执行它时遇到异常,当我关闭此连接时,是否会释放所有锁并发生回滚?

- B. Teja。

3 个答案:

答案 0 :(得分:4)

你问题的直接答案是:不。如果发生异常,您必须手动调用rollback方法。在此之前,您必须setAutoCommitfalse。默认情况下,自动提交设置为true。如果自动提交设置为true,则无法执行rollback,系统会exception告诉您。

稍后请不要忘记将autoCommit设置回true,否则您可能会遇到其他方法的不可预期的结果。

以下是有关如何实现此功能的示例。这只是草图,您应该更多地关注如何处理connectionprepared statmentexception等等。

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();
    }
}

答案 1 :(得分:2)

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

t1是一个空表,其中n1 INT(11)为非空列,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行

答案 2 :(得分:1)

狡猾的, 执行批处理时强烈不推荐autocommit=true

话虽如此,我建议使用getUpdateCount()并构建逻辑来执行剩余。

最后commit