在 JDBC 應用程序中,JDBC 語句對象用于將 SQL 語句發(fā)送到數(shù)據(jù)庫服務器。一個語句對象與一個連接相關聯(lián),應用程序與數(shù)據(jù)庫服務器之間的通信由語句對象來處理。
JDBC 中有三種類型的語句對象:
語句對象與一個連接相關聯(lián),所以要創(chuàng)建一個語句對象,首先應該建立一個數(shù)據(jù)庫連接。
清單 1 中的代碼示例演示了如何創(chuàng)建連接:
清單 1.裝載 Informix 驅動程序并創(chuàng)建一個連接的代碼示例
Connection con = null; try { Class.forName("com.informix.jdbc.IfxDriver"); String url = "jdbc:informix-sqli://hostname:port_number/dbname: informixserver=servername; userid=userid;password=pwd;"; con = DriverManager.getConnection(url); } |
現(xiàn)在逐個考察這三種類型的語句對象。
![]() ![]() |
可以使用連接的 createStatement
方法創(chuàng)建這種語句。這種語句專用于不需要傳遞任何值作為參數(shù)的 SQL 語句。
清單 2. 演示創(chuàng)建語句的示例代碼
Statement stmt = con.createStatement(); cmd = "create database testDB;"; rc = stmt.executeUpdate(cmd); stmt.close(); |
![]() ![]() |
![]() |
預置語句是 statement 類的一個子類。預置語句與 statement 類的主要區(qū)別在于,前者可以只編譯和優(yōu)化一次,然后通過設置不同的參數(shù)值多次使用。所以,如果想多次執(zhí)行一條語句,那么預置語句是更好的選擇。由于已經預先編譯好,所以減少了執(zhí)行時間。因此,預置語句的優(yōu)點是,它不僅包含一條 SQL 語句,而且還是一條預先編譯好的 SQL 語句。另一個區(qū)別是,SQL 語句在創(chuàng)建后就被提供給預置語句。
清單 3. 解釋預置語句的示例代碼
PreparedStatement pstmt = con.prepareStatement("UPDATE tab1 "+ "set col1 = ? where key = 1"); pstmt.setShort(1, (short)2); int rowcount = pstmt.executeUpdate(); |
在此,同一個預置語句可用于不同的 col1 值。參數(shù)一旦設定,它的值將保持不變,直到被重新設置或者 clearParameters
被調用。這項特性使得預置語句可以用于批量處理 INSERT
/UPDATE
。
通過設置多個值,批量更新特性提高了需要多次執(zhí)行的語句的性能。這樣可以將多個更新操作提交到一個數(shù)據(jù)源并進行一次性處理。語句對象也可以使用批量更新。但語句對象提交不同的 SQL 語句進行批處理,而預置語句提交的是一組參數(shù)。
清單 4 顯示了如何使用預置語句進行批量插入:
清單 4. 演示批量更新的示例代碼
PreparedStatement pst = conn.prepareStatement("insert into tab1 values (?)"); for loop.... { pst.setInt (1, i); pst.addBatch(); } pst.executeBatch(); |
addBatch
方法將語句添加到一個緩存中,然后使用 executeBatch()
方法轉儲到數(shù)據(jù)庫中。所以它節(jié)省了語句的編譯/優(yōu)化,因為它只編譯一次(對于預置語句),而且還節(jié)省了與服務器之間的往返,因為它一次性發(fā)送了批量插入。
![]() ![]() |
這是調用 SQL 語句的第三種方法,它提供了一種從 Java™ 程序中調用服務器上的存儲過程的方式。可調用語句也需要先作準備,然后使用 set 方法設置它們的參數(shù)。可以通過以下兩種方式設置參數(shù)值:
- 順序位置
- 命名參數(shù)
順序位置是傳統(tǒng)的參數(shù)設置方式,它根據(jù)參數(shù)在 CallableStatements 中的位置來設置參數(shù)。但是,命名參數(shù)則提供了更大的靈活性,它允許根據(jù)名稱而不是順序位置來設置參數(shù)。在調用例程時,必須以名稱或順序格式指定 CallableStatement 的參數(shù)。例如,如果對一個參數(shù)使用了參數(shù)名稱,那么對所有其他參數(shù)也必須使用參數(shù)名稱。
在調用具有許多參數(shù),而且其中一些參數(shù)有默認值的存儲過程時,命名參數(shù)特別有用。如果過程是惟一的,那么可以省略有默認值的參數(shù),并且可以按任意順序輸入?yún)?shù)。命名參數(shù)使應用程序更加健壯,所以,即使存儲過程中參數(shù)的順序發(fā)生了改變,也不必修改應用程序。
JDBC 驅動程序提供了 DatabaseMetaData.supportsNamedParameters()
方法來確認驅動程序和 RDMS 是否支持 CallableStatement 中的命名參數(shù)。如果支持命名參數(shù),則系統(tǒng)返回 true。例如:
清單 5. supportsNamedParameters() 的使用
Connection myConn = . . . // connection to the RDBMS for Database DatabaseMetaData dbmd = myConn.getMetaData(); if (dbmd.supportsNamedParameters() == true) { System.out.println("NAMED PARAMETERS FOR CALLABLE" + "STATEMENTS IS SUPPORTED"); } |
可以使用 DatabaseMetaData
的 getprocedureColumns
獲取存儲過程的參數(shù)名稱,該方法的定義如清單 6 所示:
清單 6. getProcedureColumn() 方法的使用
Connection myConn = . . . // connection to the RDBMS for Database . . DatabaseMetaData dbmd = myConn.getMetaData(); ResultSet rs = dbmd.getProcedureColumns( "myDB", schemaPattern, procedureNamePattern, columnNamePattern); rs.next() { String parameterName = rs.getString(4); - - - or - - - String parameterName = rs.getString("COLUMN_NAME"); - - - System.out.println("Column Name: " + parameterName); |
與 getProcedureColumns()
方法的參數(shù)相匹配的所有列的名稱都將被顯示。
清單 7 顯示了 CallableStatements 中的命名參數(shù)的使用。
創(chuàng)建存儲過程
清單 7. 可調用 OUT 參數(shù)的使用
create procedure createProductDef(productname varchar(64), productdesc varchar(64), listprice float, minprice float, out prod_id float); . . . let prod_id="value for prod_id"; end procedure; |
清單 8 中的 Java 代碼首先創(chuàng)建一個有 5 個參數(shù)的 CallableStatement,這 5 個參數(shù)與存儲過程中的參數(shù)相對應。JDBC 調用的括號中的問號字符 (?) 對參數(shù)進行引用。設置或注冊所有的參數(shù)。使用格式 cstmt.setString("arg", name); 命名參數(shù),其中 arg 是相應的存儲過程中的參數(shù)名稱。這里不需要按照存儲過程中的參數(shù)順序來命名參數(shù)。
清單 8. 可調用命名參數(shù)的使用
String sqlCall = "{call CreateProductDef(?,?,?,?,?)}"; CallableStatement cstmt = conn.prepareCall(sqlCall); cstmt.setString("productname", name); // Set Product Name. cstmt.setString("productdesc", desc); // Set Product Description. cstmt.setFloat("listprice", listprice); // Set Product ListPrice. cstmt.setFloat("minprice", minprice); // Set Product MinPrice. // Register out parameter which should return the product is created. cstmt.registerOutParameter("prod_id", Types.FLOAT); // Execute the call. cstmt.execute(); // Get the value of the id from the OUT parameter: prod_id float id = cstmt.getFloat("prod_id"); |
如果 CallableStatement 中的參數(shù)數(shù)量少于存儲過程中的參數(shù)數(shù)量,那么剩下的參數(shù)必須有默認值。不需要為有默認值的參數(shù)設置值,因為服務器會自動使用默認值。例如,如果一個存儲過程有 10 個參數(shù),其中 4 個參數(shù)沒有默認值,6 個參數(shù)有默認值,那么在 CallableStatement 中必須至少有 4 個問號。也可以使用 5 個、6 個或至多 10 個問號。在下面這個惟一的存儲過程中,參數(shù) listprice
和 minprice
有默認值:
清單 9. 創(chuàng)建包括具有默認值的參數(shù)的過程
create procedure createProductDef(productname varchar(64), productdesc varchar(64), listprice float default 100.00, minprice float default 90.00, out prod_id float); . . . let prod_id = value for prod_id; end procedure; |
清單 10 中的 Java 代碼使用少于存儲過程中參數(shù)數(shù)量的參數(shù)(存儲過程中有 5 個參數(shù),而代碼中只使用 4 個參數(shù))調用存儲過程。由于 listprice
有一個默認值,因此可以在 CallableStatement 中省略它。
清單 10. 默認參數(shù)的使用
String sqlCall = "{call CreateProductDef(?,?,?,?)}"; // 4 params for 5 args CallableStatement cstmt = conn.prepareCall(sqlCall); cstmt.setString("productname", name); // Set Product Name. cstmt.setString("productdesc", desc); // Set Product Description. cstmt.setFloat("minprice", minprice); // Set Product MinPrice. // Register out parameter which should return the product id created. cstmt.registerOutParameter("prod_id", Types.FLOAT); // Execute the call. cstmt.execute(); // Get the value of the id from the OUT parameter: prod_id float id = cstmt.getFloat("prod_id"); |
如果可調用語句包含 OUT
或 INOUT
參數(shù),那么需要使用 CallableStatement 的 registerOutParameter
注冊這些參數(shù)。清單 11 使用 out
參數(shù) prod_id
創(chuàng)建一個具有 OUT
參數(shù)的存儲過程。類似地,可以使用關鍵字 INOUT
創(chuàng)建 INOUT
參數(shù)。
清單 11. INOUT 和 OUT 參數(shù)的使用
create procedure createProductDef(productname varchar(64), productdesc varchar(64), inout listprice float default 100.00, minprice float default 90.00, out prod_id float); |
清單 12 使用 CallableStatements registerOutparameter
方法注冊 CallableStatement 的 out
參數(shù)。
清單 12. 使用 CallableStatement 注冊 OUT 參數(shù)
cstmt.registerOutParameter("prod_id", Types.FLOAT); |
清單 13 將使用命名參數(shù)特性的所有語句合并在一起:
清單 13. 演示命名參數(shù)功能的程序
package Callable; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.sql.Types; public class out1 { static Connection conn; public static void main(String[] args) { getConnect(); System.out.println("Connection Established"); createProc(); runthis(); System.out.println("\n=============Finished============="); System.exit(0); } private static void getConnect() { try { Class.forName("com.informix.jdbc.IfxDriver"); String url = "jdbc:informix-sqli://host name or ip :porn number/database name:informixserver=dbservername;"; System.out.println("URL: "+url); conn = DriverManager.getConnection(url); } catch( Exception e ) { e.printStackTrace(); System.exit(1); } } private static void createProc() { String str=null; Statement stmt = null; try { stmt = conn.createStatement(); } catch (SQLException e2) { e2.printStackTrace(); } str="drop function c_out_proc"; try { stmt.executeUpdate (str); } catch (SQLException e1) { } str = "create function c_out_proc ( i int, OUT d varchar(20) ) \n" + "returning float; \n" + "define f float; \n" + "let d= \"Hello OUT\"; \n" + "let f=i*2; \n" + "return f; \n" + "end function; \n"; try { stmt.executeUpdate (str); System.out.println("Function created \n"); } catch (SQLException e) { System.out.println("Error on creating function: " + e.toString()); System.exit(1); } } private static void runthis() { CallableStatement cstmt = null; String command = "{? = call c_out_proc(?, ?)} "; try { cstmt = conn.prepareCall (command); cstmt.setInt(1, 2); cstmt.registerOutParameter(2, Types.VARCHAR); ResultSet rs = cstmt.executeQuery(); if (rs == null) { System.out.println("rs is null *** this is BAD."); System.exit(0); } else { rs.next(); System.out.println(rs.getFloat(1)); System.out.println(cstmt.getString(2)); } } catch (SQLException e) { e.printStackTrace(); } } } |