JDBC批处理更新
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事务中执行批处理更新。在事务内部执行时,我们可以确保执行所有更新或者不执行任何更新。万一其中一个更新失败,任何成功的更新都可以回滚。