使用准备好的语句
此页面包含以下主题:
准备好的报表概述
有时使用PreparedStatement
对象将 SQL 语句发送到数据库更方便。这种特殊的语句类型来自您已经知道的更通用的类Statement
。
如果要多次执行Statement
对象,通常可以减少使用PreparedStatement
对象的执行时间。
PreparedStatement
对象的主要 Feature 是,与Statement
对象不同,它在创建时会被赋予一条 SQL 语句。这样做的好处是,在大多数情况下,此 SQL 语句会立即发送到 DBMS 进行编译。结果,PreparedStatement
对象不仅包含 SQL 语句,还包含已预编译的 SQL 语句。这意味着执行PreparedStatement
时,DBMS 可以只运行PreparedStatement
SQL 语句而不必先对其进行编译。
尽管PreparedStatement
对象可以用于不带参数的 SQL 语句,但是您可能最常将它们用于带有参数的 SQL 语句。使用带有参数的 SQL 语句的优点在于,您可以使用同一条语句,并在每次执行时为其提供不同的值。以下各节为示例。
以下方法CoffeesTable.updateCoffeeSales
将每种类型的咖啡在本周内的销售磅数存储在SALES
列中,并更新每种类型的TOTAL
列中的销售磅数总数:
public void updateCoffeeSales(HashMap<String, Integer> salesForWeek)
throws SQLException {
PreparedStatement updateSales = null;
PreparedStatement updateTotal = null;
String updateString =
"update " + dbName + ".COFFEES " +
"set SALES = ? where COF_NAME = ?";
String updateStatement =
"update " + dbName + ".COFFEES " +
"set TOTAL = TOTAL + ? " +
"where COF_NAME = ?";
try {
con.setAutoCommit(false);
updateSales = con.prepareStatement(updateString);
updateTotal = con.prepareStatement(updateStatement);
for (Map.Entry<String, Integer> e : salesForWeek.entrySet()) {
updateSales.setInt(1, e.getValue().intValue());
updateSales.setString(2, e.getKey());
updateSales.executeUpdate();
updateTotal.setInt(1, e.getValue().intValue());
updateTotal.setString(2, e.getKey());
updateTotal.executeUpdate();
con.commit();
}
} catch (SQLException e ) {
JDBCTutorialUtilities.printSQLException(e);
if (con != null) {
try {
System.err.print("Transaction is being rolled back");
con.rollback();
} catch(SQLException excep) {
JDBCTutorialUtilities.printSQLException(excep);
}
}
} finally {
if (updateSales != null) {
updateSales.close();
}
if (updateTotal != null) {
updateTotal.close();
}
con.setAutoCommit(true);
}
}
创建 PreparedStatement 对象
下面创建一个带有两个 Importing 参数的PreparedStatement
对象:
String updateString =
"update " + dbName + ".COFFEES " +
"set SALES = ? where COF_NAME = ?";
updateSales = con.prepareStatement(updateString);
为 PreparedStatement 参数提供值
在执行PreparedStatement
对象之前,必须提供值代替问号占位符(如果有)。通过调用PreparedStatement
类中定义的 setter 方法之一来执行此操作。以下语句在名为updateSales
的PreparedStatement
中提供了两个问号占位符:
updateSales.setInt(1, e.getValue().intValue());
updateSales.setString(2, e.getKey());
每个设置方法的第一个参数指定问号占位符。在此的示例setInt
指定第一个占位符,而setString
指定第二个占位符。
在为参数设置一个值之后,它将保留该值,直到将其重置为另一个值,或者调用方法clearParameters
。以下代码段使用PreparedStatement
对象updateSales
来说明在重设其中一个参数的值并使另一个参数保持不变后重用已准备好的语句:
// changes SALES column of French Roast
//row to 100
updateSales.setInt(1, 100);
updateSales.setString(2, "French_Roast");
updateSales.executeUpdate();
// changes SALES column of Espresso row to 100
// (the first parameter stayed 100, and the second
// parameter was reset to "Espresso")
updateSales.setString(2, "Espresso");
updateSales.executeUpdate();
使用循环设置值
通过使用for
循环或while
循环来设置 Importing 参数的值,通常可以使编码更容易。
CoffeesTable.updateCoffeeSales
方法使用 for-each 循环重复设置PreparedStatement
对象updateSales
和updateTotal
中的值:
for (Map.Entry<String, Integer> e : salesForWeek.entrySet()) {
updateSales.setInt(1, e.getValue().intValue());
updateSales.setString(2, e.getKey());
// ...
}
方法CoffeesTable.updateCoffeeSales
接受一个参数HashMap
。 HashMap
参数中的每个元素都包含一种咖啡的名称以及本周售出的这种咖啡的磅数。 for-each 循环遍历HashMap
参数的每个元素,并在updateSales
和updateTotal
中设置适当的问号占位符。
执行 PreparedStatement 对象
与Statement
对象一样,要执行PreparedStatement
对象,请调用一条 execute 语句:executeQuery
(如果查询仅返回一个ResultSet
(例如SELECT
SQL 语句),executeUpdate
如果查询不返回ResultSet
(例如UPDATE
SQL 语句) ,如果查询可能返回多个ResultSet
对象,则返回execute
。 CoffeesTable.updateCoffeeSales
中的两个PreparedStatement
对象都包含UPDATE
SQL 语句,因此都通过调用executeUpdate
来执行:
updateSales.setInt(1, e.getValue().intValue());
updateSales.setString(2, e.getKey());
updateSales.executeUpdate();
updateTotal.setInt(1, e.getValue().intValue());
updateTotal.setString(2, e.getKey());
updateTotal.executeUpdate();
con.commit();
用于执行updateSales
和updateTotals
的参数不会提供给executeUpdate
;两个PreparedStatement
对象已经包含要执行的 SQL 语句。
注意 :在CoffeesTable.updateCoffeeSales
的开头,自动提交 Pattern 设置为 false:
con.setAutoCommit(false);
因此,在调用方法commit
之前,不会提交任何 SQL 语句。有关自动提交 Pattern 的更多信息,请参见Transactions。
executeUpdate 方法的返回值
executeQuery
返回包含发送到 DBMS 的查询结果的ResultSet
对象,而executeUpdate
的返回值是int
值,该值指示表中有多少行被更新。例如,以下代码显示了将executeUpdate
的返回值分配给变量n
:
updateSales.setInt(1, 50);
updateSales.setString(2, "Espresso");
int n = updateSales.executeUpdate();
// n = 1 because one row had a change in it
表COFFEES
已更新;值 50 替换Espresso
行的SALES
列中的值。该更新影响表中的一行,因此n
等于 1.
当方法executeUpdate
用于执行 DDL(数据定义语言)语句时(例如在创建表时),它返回int
值 0.因此,在下面的代码片段中,该代码执行用于创建表的 DDL 语句COFFEES
,n
被赋值为 0:
// n = 0
int n = executeUpdate(createTableCoffees);
请注意,当executeUpdate
的返回值为 0 时,它可能意味着以下两种情况之一:
-
执行的语句是影响零行的更新语句。
-
执行的语句是 DDL 语句。