存儲過程定義
CREATE PROCEDURE InsertUser
@loginName VARCHAR(50),
@realName VARCHAR(50),
@passWord VARCHAR(50),
@userId INT OUTPUT
AS
SET NOCOUNT ON;
IF EXISTS (SELECT userID FROM tsysuser WHERE loginName = @loginName)
RETURN 0; -- 已存在
ELSE BEGIN
INSERT INTO tsysuser (loginName, realName, password)
VALUES (@loginName, @realName, @passWord);
SET @userId = @@IDENTITY;
RETURN 1; -- 成功
END;
GO
Java 調用代碼
Connection conn = getConnection(); // 你的連接池獲取方法
CallableStatement proc = null;
String sql = "{? = CALL InsertUser(?, ?, ?, ?)}"; // 返回值佔位符在最前
try {
proc = conn.prepareCall(sql);
// 1. 註冊返回值(第一個參數)
proc.registerOutParameter(1, Types.INTEGER);
// 2. 設置輸入參數(對應存儲過程第 1~3 參數)
proc.setString(2, "sa");
proc.setString(3, "sd");
proc.setString(4, "sd");
// 3. 註冊輸出參數(存儲過程第 4 參數 @userId)
proc.registerOutParameter(5, Types.INTEGER);
// 4. 執行
proc.execute();
// 5. 獲取結果
int returnCode = proc.getInt(1); // RETURN 值
int newUserId = proc.getInt(5); // OUTPUT 參數
System.out.println("Return: " + returnCode + ", New UserId: " + newUserId);
if (returnCode == 1) {
// 業務成功邏輯
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (proc != null) try { proc.close(); } catch (SQLException ignore) {}
if (conn != null) try { conn.close(); } catch (SQLException ignore) {}
}
關鍵點總結
| 項目 | 說明 |
| SQL 語法 | {? = CALL proc(?, ?, ?)} 返回值佔位符在最前,參數順序與存儲過程定義一致 |
| 參數索引 | JDBC 參數索引從 1 開始:1=返回值,2~n=輸入,n+1~=輸出 |
| registerOutParameter | 返回值與 OUTPUT 參數都必須註冊,類型對應 java.sql.Types |
| SET NOCOUNT ON | 存儲過程首行加上,避免 DONE_IN_PROC 干擾 ResultSet 處理 |
| @@IDENTITY vs SCOPE_IDENTITY() | 觸發器環境下用 SCOPE_IDENTITY() 更安全 |
| 資源關閉 | finally 塊關閉 CallableStatement 和 Connection |
與 Oracle 差異對照
| 特性 | SQL Server | Oracle |
| 返回值 | RETURN int,JDBC 當第一個參數 | 無 RETURN,用第一個 OUT 參數約定 |
| 調用語法 | {? = CALL proc(...)} | {CALL proc(...)} 或 BEGIN proc(...); END; |
| 標識列 | IDENTITY / @@IDENTITY | SEQUENCE / RETURNING INTO |
| 包/重載 | 不支持 | 支持 Package 重載 |
最佳實踐
- 連接池複用
Connection,避免頻繁創建
- 存儲過程命名規範:
usp_模塊_動作(如 usp_User_Insert)
- 輸入參數驗證前置(長度、格式、業務規則)
- 異常情況拋出自定義錯誤碼(
RAISERROR(50001, 16, 1, 'msg')),Java 端捕獲 SQLException.getErrorCode()
- 批量操作改用 TVP(表值參數)而非循環調用