| 有时用PreparedStatement对象发送SQL语句给数据库更加方便有效。这种特殊类型的语句派生自更加通用的类Statement。
何时使用PreparedStatement对象
如果想多次执行一个Statement对象,使用PreparedStatement对象代替Statement对象一般会缩短执行时间。
与Statement对象不同的是,PreparedStatement对象的主要特性是创建时就指定SQL语句。因此,它的好处在于多数场合下,这条语句马上发送到DBMS进行编译。其结果是PreparedStatement对象不只是包含一条SQL语句,而是包含一条预编译过的SQL语句。这表明PreparedStatement执行时,DBMS不必编译就可直接运行PreparedStatement的SQL语句。
尽管PreparedStatement对象可用于不带参数的SQL语句,但在多数场合是用于带参数的SQL语句。使用带参数的SQL语句的优点是,您可以使用相同的语句,只是在每次执行时提供不同的参数值。下面几节您会看到这种例子。
创建PreparedStatement对象
如同Statement对象,您可用Connection方法创建PreparedStatement对象。使用前面例子打开的连接con,可编写如下代码创建带有两个输入参数的PreparedStatement对象:
PreparedStatement updateSales = con.prepareStatement(
"UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ?");
现在变量updateSales包含了SQL语句“UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ?”,在多数情况下,这条语句也已发送到DBMS预编译。
为PreparedStatement参数提供值
如果有参数,那在执行PreparedStatement对象前还应使用值来替代问号占位符。通过调用PreparedStatement类中定义的一种setXXX方法就可完成该替代。如果想用来替代问号的值是Java整数值,那么可调用SetInt。如果想用来替代问号的值是Java字符串,就可调用setString方法,如此等等。在Java程序设计语言中,一般情况下一种类型有一种setXXX方法。
使用前面例子创建的PreparedStatement对象updateSales,下面代码将把第一个问号占位符设为值75的Java整数:
updateSales.setInt(1, 75);
从例子中可以猜测到,setXXX方法的第一个参数表示设置的是哪个问号占位符,第二个参数表示设置值。下一例子将第二个占位符参数设为字符串“Colombian”:
updateSales.setString(2, "Colombian");
为两个输入参数设置值后,updateSales中的SQL语句就等价于前面例子使用的String对象updateString中的SQL语句。因此下面两个代码段等价:
代码段1:
String updateString = "UPDATE COFFEES SET SALES = 75 " +
"WHERE COF_NAME LIKE 'Colombian'";
stmt.executeUpdate(updateString);
代码段2:
PreparedStatement updateSales = con.prepareStatement(
"UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ? ");
updateSales.setInt(1, 75);
updateSales.setString(2, "Colombian");
updateSales.executeUpdate():
这里使用executeUpdate方法执行Statement对象stmt和PreparedStatement对象updateSales。但执行updateSales时并没有为executeUpdate提供参数,因为updateSales已经包含了要执行的SQL语句。
看到这些例子您可能会这样想,既然简单语句(只有一条)涉及较少的步骤,那为什么要用带参数的PreparedStatement对象?如果只想更新SALES列一两次,那就没必要使用带输入参数的SQL语句。相反,如果要经常更新数据,特别是在可用for或while循环为参数设置连续值的情况下,使用PreparedStatement就要容易得多。本节后面会有这样的例子。
一旦为参数设置了数值,它将一直保存这个值,直到它重设为另一个值或调用了clearParameters方法为止。下面的代码段使用PreparedStatement对象updateSales例示了在重设一个参数值、保持另一参数不变后重用一条预备语句:
updateSales.setInt(1, 100);
updateSales.setString(2, "French_Roast");
updateSales.executeUpdate();
// changes SALES column of French Roast row to 100
updateSales.setString(2, "Espresso");
updateSales.executeUpdate();
// changes SALES column of Espresso row to 100 (the first
// parameter stayed 100, and the second parameter was reset
// to "Espresso")
使用循环设置参数值
通过使用for或while循环设置输入参数值通常可使编码更容易些。
下面代码段例示使用for循环设置PreparedStatement对象updateSales中的参数值。数组salesForWeek保存每周销售量。这些销售量对应数组coffees中的咖啡名称,因此salesForWeek的第一个数量(175)应用于coffees中的第一个咖啡名称(“Colombian”),salesForWeek的第二个数量(150)应用于coffees中的第二个咖啡名称(“French_Roast”),以此类推。这段代码展示更新COFFEES表中所有咖啡的SALES列:
PreparedStatement updateSales;
String updateString = "update COFFEES " +
"set SALES = ? where COF_NAME like ?";
updateSales = con.prepareStatement(updateString);
int [] salesForWeek = {175, 150, 60, 155, 90};
String [] coffees = {"Colombian", "French_Roast", "Espresso",
"Colombian_Decaf", "French_Roast_Decaf"};
int len = coffees.length;
for(int i = 0; i < len; i++) {
updateSales.setInt(1, salesForWeek[i]);
updateSales.setString(2, coffees[i]);
updateSales.executeUpdate();
}
当咖啡馆老板想更新下一周销售数量时就可用相同代码做模板。但他要按正确顺序在数组salesForWeek中输入新的销售数量。数组coffees中的咖啡名称保持不变,因此它们不需要变更(在实际的应用程序中,这些数值可能从用户那里输入而不是从初始化Java数组输入)。
executeUpdate方法的返回值
尽管executeQuery返回一个ResultSet对象,其中包含发送到DBMS的查询的结果,但executeUpdate返回的是一个整数值,指出了表中已更新的行数。例如,下面的代码显示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表被更新了,即Espresso行的SALES列中的值被替换为50。此更新影响表中的一行,因此n等于1。
当executeUpdate方法执行DLL语句(如创建表)时将返回整数0。因此下面的代码段中,执行创建COFFEES表的DLL语句后,赋给n的值是0:
int n = executeUpdate(createTableCoffees); // n = 0
注意,executeUpdate的返回值为0表明如下两种情况:(1) 执行的语句是一个不影响任何行的更新语句;(2) 执行的是一个DLL语句。 |