初次開發 Java 專案時,網路上找到的呼叫 SQL Server 預存程序範例多半採用索引綁定(setXXX(int parameterIndex, XXX x)),閱讀性較差。本文提供一個完整採用參數名稱綁定(setXXX(String parameterName, XXX x))的實作範例,包含建表腳本、預存程序與 JSP 完整程式碼,供開發者參考。
CREATE TABLE [BookUser] (
[UserID] [int] IDENTITY (1, 1) NOT NULL,
[UserName] [varchar] (50) COLLATE Chinese_PRC_CI_AS NOT NULL,
[Title] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NOT NULL,
[Guid] [uniqueidentifier] NOT NULL CONSTRAINT [DF_BookUser_Guid] DEFAULT (newid()),
[BirthDate] [datetime] NOT NULL,
[Description] [ntext] COLLATE Chinese_PRC_CI_AS NOT NULL,
[Photo] [image] NULL,
[Other] [varchar] (50) COLLATE Chinese_PRC_CI_AS NULL CONSTRAINT [DF_BookUser_Other] DEFAULT ('預設值'),
CONSTRAINT [PK_BookUser] PRIMARY KEY CLUSTERED ([UserID]) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
CREATE PROCEDURE InsertUser
@UserName varchar(50),
@Title varchar(255),
@Guid uniqueidentifier,
@BirthDate DateTime,
@Description ntext,
@Photo image,
@Other nvarchar(50),
@UserID int output
As
BEGIN
SET NOCOUNT ON
IF EXISTS (SELECT UserID FROM BookUser WHERE UserName = @UserName)
RETURN 0
ELSE
BEGIN
INSERT INTO BookUser (UserName, Title, Guid, BirthDate, Description, Photo, Other)
VALUES (@UserName, @Title, @Guid, @BirthDate, @Description, @Photo, @Other)
SET @UserID = @@IDENTITY
RETURN 1
END
END
GO
以下採用 Microsoft 官方 JDBC 驅動程式(com.microsoft.sqlserver.jdbc.SQLServerDriver),請至 Microsoft 下載中心取得最新版本。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.sql.*" %>
<!DOCTYPE html>
<html xmlns="https://www.w3.org/1999/xhtml">
<head></head>
<body>
<%
// 載入驅動
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
String url = "jdbc:sqlserver://localhost:1433;databaseName=Book;user=sa;password=";
// 使用參數名稱綁定,注意第一個 ? 為回傳值,最後一個 ? 為 OUTPUT 參數
String sql = "{? = call InsertUser(?,?,?,?,?,?,?,?)}";
Connection conn = null;
CallableStatement stmt = null;
try {
conn = DriverManager.getConnection(url);
stmt = conn.prepareCall(sql);
// 準備參數
java.util.UUID guid = java.util.UUID.randomUUID();
String filePath = application.getRealPath("") + "testlogo.gif";
java.io.FileInputStream fis = new java.io.FileInputStream(filePath);
java.sql.Date birthDate = java.sql.Date.valueOf("2007-09-09");
// 以參數名稱設定值(需與預存程序參數名一致)
stmt.setString("UserName", "mengxianhui"); // 預存程序驗證 UserName 唯一性
stmt.setString("Title", "孟憲會");
stmt.setString("Guid", guid.toString());
stmt.setDate("BirthDate", birthDate);
stmt.setString("Description", "【孟子E章】");
stmt.setBinaryStream("Photo", fis, fis.available());
stmt.setString("Other", null);
// 註冊輸出參數:1 為回傳值,UserID 為 OUTPUT 參數
stmt.registerOutParameter(1, java.sql.Types.INTEGER);
stmt.registerOutParameter("UserID", java.sql.Types.INTEGER);
stmt.execute();
int returnValue = stmt.getInt(1);
int userId = stmt.getInt("UserID");
if (returnValue == 1) {
out.println("<li>新增成功!");
out.println("<li>UserID = " + userId);
out.println("<li>returnValue = " + returnValue);
} else {
out.println("<li>新增失敗!用戶名可能已存在。");
}
fis.close();
} catch (Exception ex) {
out.println("錯誤:" + ex.getLocalizedMessage());
} finally {
try {
if (stmt != null) { stmt.close(); stmt = null; }
if (conn != null) { conn.close(); conn = null; }
} catch (Exception e) {
e.printStackTrace();
}
}
%>
</body>
</html>
setString("UserName", ...) 而非索引,程式碼更易讀、維護。registerOutParameter("UserID", Types.INTEGER) 以名稱註冊,getInt("UserID") 取值。setBinaryStream 上傳圖片,需注意關閉串流。IF EXISTS 檢查 UserName,重複則回傳 0。若發生「找不到驅動程式」錯誤,請確認已將 sqljdbc.jar 或 mssql-jdbc.jar 加入專案類別路徑。資料庫連線字串中的 databaseName、user、password 請依實際環境調整。