Java呼叫SQL Server的儲存過程詳解
本文較長,包含了如下幾部分
-
1使用不帶引數的儲存過程
使用 JDBC 驅動程式呼叫不帶引數的儲存過程時,必須使用 call SQL 轉義序列。不帶引數的 call 轉義序列的語法如下所示: {call procedure-name} 作為例項,在 SQL Server 2005 AdventureWorks 示例資料庫中建立以下儲存過程:
CREATEPROCEDURE GetContactFormalNames ASBEGIN SELECTTOP10 Title +''+ FirstName +''+ LastName AS FormalName2使用帶有輸入引數的儲存過程
使用 JDBC 驅動程式呼叫帶引數的儲存過程時,必須結合
構造 call 轉義序列時,請使用 ?(問號)字元來指定 IN 引數。此字元充當要傳遞給該儲存過程的引數值的佔位符。可以使用 SQLServerPreparedStatement 類的 setter 方法之一為引數指定值。可使用的 setter 方法由 IN 引數的資料型別決定。 向 setter 方法傳遞值時,不僅需要指定要在引數中使用的實際值,還必須指定引數在儲存過程中的序數位置。例如,如果儲存過程包含單個 IN 引數,則其序數值為 1。如果儲存過程包含兩個引數,則第一個序數值為 1,第二個序數值為 2。 作為如何呼叫包含 IN 引數的儲存過程的例項,使用 SQL Server 2005 AdventureWorks 示例資料庫中的 uspGetEmployeeManagers 儲存過程。此儲存過程接受名為 EmployeeID 的單個輸入引數(它是一個整數值),然後基於指定的 EmployeeID 返回僱員及其經理的遞迴列表。下面是呼叫此儲存過程的 Java 程式碼:
publicstaticvoid executeSprocInParams(Connection con) ...{ try...{ PreparedStatement pstmt = con.prepareStatement("{call dbo.uspGetEmployeeManagers(?)}"); pstmt.setInt(1, 50); ResultSet rs = pstmt.executeQuery(); while (rs.next()) ...{ System.out.println("EMPLOYEE:"); System.out.println(rs.getString("LastName") +", "+ rs.getString("FirstName")); System.out.println("MANAGER:"); System.out.println(rs.getString("ManagerLastName") +", "+ rs.getString("ManagerFirstName")); System.out.println(); } rs.close(); pstmt.close(); } catch (Exception e) ...{ e.printStackTrace(); }}3使用帶有輸出引數的儲存過程
使用 JDBC 驅動程式呼叫此類儲存過程時,必須結合 SQLServerConnection 類的 prepareCall 方法使用 call SQL 轉義序列。帶有 OUT 引數的 call 轉義序列的語法如下所示: {call procedure-name[([parameter][,[parameter]]...)]} 構造 call 轉義序列時,請使用 ?(問號)字元來指定 OUT 引數。此字元充當要從該儲存過程返回的引數值的佔位符。要為 OUT 引數指定值,必須在執行儲存過程前使用 SQLServerCallableStatement 類的 registerOutParameter 方法指定各引數的資料型別。 使用 registerOutParameter 方法為 OUT 引數指定的值必須是 java.sql.Types 所包含的 JDBC 資料型別之一,而它又被對映成本地 SQL Server 資料型別之一。有關 JDBC 和 SQL Server 資料型別的詳細資訊,請參閱瞭解 JDBC 驅動程式資料型別。 當您對於 OUT 引數向 registerOutParameter 方法傳遞一個值時,不僅必須指定要用於此引數的資料型別,而且必須在儲存過程中指定此引數的序號位置或此引數的名稱。例如,如果儲存過程包含單個 OUT 引數,則其序數值為 1;如果儲存過程包含兩個引數,則第一個序數值為 1,第二個序數值為 2。 作為例項,在 SQL Server 2005 AdventureWorks 示例資料庫中建立以下儲存過程: 根據指定的整數 IN 引數 (employeeID),該儲存過程也返回單個整數 OUT 引數 (managerID)。根據 HumanResources.Employee 表中包含的 EmployeeID,OUT 引數中返回的值為 ManagerID。 在下面的例項中,將向此函式傳遞 AdventureWorks 示例資料庫的開啟連線,然後使用 execute 方法呼叫 GetImmediateManager 儲存過程:
publicstaticvoid executeStoredProcedure(Connection con) ...{ try...{ CallableStatement cstmt = con.prepareCall("{call dbo.GetImmediateManager(?, ?)}"); cstmt.setInt(1, 5); cstmt.registerOutParameter(2, java.sql.Types.INTEGER); cstmt.execute(); System.out.println("MANAGER ID: "+ cstmt.getInt(2)); } catch (Exception e) ...{ e.printStackTrace(); }} 本示例使用序號位置來標識引數。或者,也可以使用引數的名稱(而非其序號位置)來標識此引數。下面的程式碼示例修改了上一個示例,以說明如何在 Java 應用程式中使用命名引數。請注意,這些引數名稱對應於儲存過程的定義中的引數名稱: CREATEPROCEDURE GetImmediateManager @employeeIDINT, @managerIDINT OUTPUTASBEGIN SELECT@managerID= ManagerID FROM HumanResources.Employee WHERE EmployeeID =@employeeIDEND儲存過程可能返回更新計數和多個結果集。Microsoft SQL Server 2005 JDBC Driver 遵循 JDBC 3.0 規範,此規範規定在檢索 OUT 引數之前應檢索多個結果集和更新計數。也就是說,應用程式應先檢索所有 ResultSet 物件和更新計數,然後使用 CallableStatement.getter 方法檢索 OUT 引數。否則,當檢索 OUT 引數時,尚未檢索的 ResultSet 物件和更新計數將丟失。
4 使用帶有返回狀態的儲存過程
使用 JDBC 驅動程式呼叫這種儲存過程時,必須結合 SQLServerConnection 類的 prepareCall 方法使用 call SQL 轉義序列。返回狀態引數的 call 轉義序列的語法如下所示: {[?=]call procedure-name[([parameter][,[parameter]]...)]} 構造 call 轉義序列時,請使用 ?(問號)字元來指定返回狀態引數。此字元充當要從該儲存過程返回的引數值的佔位符。要為返回狀態引數指定值,必須在執行儲存過程前使用 SQLServerCallableStatement 類的 registerOutParameter 方法指定引數的資料型別。 此外,向 registerOutParameter 方法傳遞返回狀態引數值時,不僅需要指定要使用的引數的資料型別,還必須指定引數在儲存過程中的序數位置。對於返回狀態引數,其序數位置始終為 1,這是因為它始終是呼叫儲存過程時的第一個引數。儘管 SQLServerCallableStatement 類支援使用引數的名稱來指示特定引數,但您只能對返回狀態引數使用引數的序號位置編號。 作為例項,在 SQL Server 2005 AdventureWorks 示例資料庫中建立以下儲存過程:
CREATEPROCEDURE CheckContactCity (@cityNameCHAR(50))ASBEGIN IF ((SELECTCOUNT(*) FROM Person.Address WHERE City =@cityName) >1) RETURN1ELSE RETURN0END該儲存過程返回狀態值 1 或 0,這取決於是否能在表 Person.Address 中找到 cityName 引數指定的城市。 在下面的例項中,將向此函式傳遞 AdventureWorks 示例資料庫的開啟連線,然後使用 execute 方法呼叫 CheckContactCity 儲存過程:
publicstaticvoid executeStoredProcedure(Connection con) ...{ try...{ CallableStatement cstmt = con.prepareCall("{? = call dbo.CheckContactCity(?)}"); cstmt.registerOutParameter(1, java.sql.Types.INTEGER); cstmt.setString(2, "Atlanta"); cstmt.execute(); System.out.println("RETURN STATUS: "+ cstmt.getInt(1)); } cstmt.close(); catch (Exception e) ...{ e.printStackTrace(); }}5 使用帶有更新計數的儲存過程
使用 SQLServerCallableStatement 類構建對儲存過程的呼叫之後,可以使用 execute 或 executeUpdate 方法中的任意一個來呼叫此儲存過程。executeUpdate 方法將返回一個 int 值,該值包含受此儲存過程影響的行數,但 execute 方法不返回此值。如果使用 execute 方法,並且希望獲得受影響的行數計數,則可以在執行儲存過程後呼叫 getUpdateCount 方法。
作為例項,在 SQL Server 2005 AdventureWorks 示例資料庫中建立以下表和儲存過程:
CREATETABLE TestTable (Col1 intIDENTITY, Col2 varchar(50), Col3 int);CREATEPROCEDURE UpdateTestTable @Col2varchar(50), @Col3intASBEGIN UPDATE TestTable SET Col2 =@Col2, Col3 =@Col3END; 在下面的例項中,將向此函式傳遞 AdventureWorks 示例資料庫的開啟連線,並使用 execute 方法呼叫 UpdateTestTable 儲存過程,然後使用 getUpdateCount 方法返回受儲存過程影響的行計數。 publicstaticvoid executeUpdateStoredProcedure(Connection con) ...{ try...{ CallableStatement cstmt = con.prepareCall("{call dbo.UpdateTestTable(?, ?)}"); cstmt.setString(1, "A"); cstmt.setInt(2, 100); cstmt.execute(); int count = cstmt.getUpdateCount(); cstmt.close(); System.out.println("ROWS AFFECTED: "+ count); } catch (Exception e) ...{ e.printStackTrace(); }}