JDBC批处理更新

时间:2020-01-09 10:36:46  来源:igfitidea点击:

JDBC批处理更新是将一批更新分组在一起,并成批发送到数据库,而不是一个接一个地发送。

一次性将一批更新发送到数据库要比逐个发送更新(等待每个更新完成)要快。发送一批更新(仅一次往返)所涉及的网络流量较少,并且数据库可能能够并行执行某些更新。与逐个执行更新相比,速度可能会很大。

我们可以批处理SQL插入,更新和删除。批量选择语句没有意义。

有两种执行JDBC批处理更新的方法:

  • 使用语句 Statement
  • 使用预声明语句 PreparedStatement

本JDBC批处理更新教程在以下各节中介绍了这两种方法。

语句批处理更新

我们可以使用Statement对象执行批量更新。我们可以使用addBatch()executeBatch()方法来实现。这是一个例子:

Statement statement = null;

try{
    statement = connection.createStatement();

    statement.addBatch("update people set firstname='John' where id=123");
    statement.addBatch("update people set firstname='Eric' where id=456");
    statement.addBatch("update people set firstname='May'  where id=789");

    int[] recordsAffected = statement.executeBatch();
} finally {
    if(statement != null) statement.close();
}

首先,使用addBatch()方法添加要在批处理中执行的SQL语句。

然后,我们使用executeBatch()执行SQL语句。 " executeBatch()"方法返回的" int []"数组是一个" int"数组,用于告诉该批处理中的每个已执行SQL语句影响了多少条记录。

PreparedStatement批处理更新

我们也可以使用PreparedStatement对象执行批量更新。 " PreparedStatement"使我们可以重复使用相同的SQL语句,并只需其中插入新参数,即可执行每次更新。这是一个例子:

String sql = "update people set firstname=? , lastname=? where id=?";

PreparedStatement preparedStatement = null;
try{
    preparedStatement =
            connection.prepareStatement(sql);

    preparedStatement.setString(1, "Gary");
    preparedStatement.setString(2, "Larson");
    preparedStatement.setLong  (3, 123);

    preparedStatement.addBatch();

    preparedStatement.setString(1, "Stan");
    preparedStatement.setString(2, "Lee");
    preparedStatement.setLong  (3, 456);

    preparedStatement.addBatch();

    int[] affectedRecords = preparedStatement.executeBatch();

}finally {
    if(preparedStatement != null) {
        preparedStatement.close();
    }
}

首先,从带有问号的SQL语句创建一个" PreparedStatement",以显示将参数值插入SQL的位置。

其次,将每组参数值插入prepareStatement中,并调用addBatch()方法。这会将参数值内部添加到批处理中。现在,我们可以添加另一组值,以将其插入到SQL语句中。将完整的批处理发送到数据库后,每组参数都将插入到SQL中并分别执行。

第三,调用executeBatch()方法,该方法执行所有批处理更新。 SQL语句和参数集将一次性发送到数据库。 " executeBatch()"方法返回的" int []"数组是一个" int"数组,用于告诉该批处理中的每个已执行SQL语句影响了多少条记录。

循环添加批处理

通常,我们会从for循环或者while循环内部将批处理添加到Statement或者PreparedStatement中。循环中的每次迭代都会添加一批。这是从for循环内部将批次添加到PreparedStatement的示例:

List<Person> persons = ... // get a list of Person objects from somewhere.

String sql = "update people set firstname=? , lastname=? where id=?";

PreparedStatement preparedStatement = null;
try{
    preparedStatement =
            connection.prepareStatement(sql);

    for(Person person : persons) {
        preparedStatement.setString(1, person.getFirstName());
        preparedStatement.setString(2, person.getLastName());
        preparedStatement.setLong  (3, person.getId());

        preparedStatement.addBatch();
    }

    int[] affectedRecords = preparedStatement.executeBatch();

}finally {
    if(preparedStatement != null) {
        preparedStatement.close();
    }
}

在上面的示例中,我们从某处获得了" Person"对象的列表。本部分未包括在示例中,因为该列表的来源与其无关。重要的是如何迭代列表,并将每个" Person"对象中的值添加到批处理中。将所有" Person"对象添加到批次后,将执行批次更新。

顺便说一下,假设使用的Person类看起来像这样:

public class Person{
    private String firstName = null;
    private String lastName  = null;
    private long   id        = -1;

    public String getFirstName() {
        return this.firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return this.lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public long getId() {
        return this.id;
    }

    public void setId(long id) {
        this.id = id;
    }
}

批量更新和事务

重要的是要记住,添加到Statement或者PreparedStatement的每个更新都是由数据库单独执行的。这意味着,其中一些可能其中之一失败之前就已经成功。现在,所有成功的语句都将应用于数据库,但其余更新可能未应用。这可能导致数据库中的数据不一致。

为避免这种情况,我们可以在JDBC事务中执行批处理更新。在事务内部执行时,我们可以确保执行所有更新或者不执行任何更新。万一其中一个更新失败,任何成功的更新都可以回滚。