JSTL SQL 標籤庫 (uri="https://java.sun.com/jsp/jstl/sql") 允許在 JSP 頁面直接執行資料庫操作,適合快速原型、小型專案、報表頁面。本文詳細解析資料源配置、查詢/更新/交易標籤、結果集屬性與分頁應用。
<%@ taglib prefix="sql" uri="https://java.sun.com/jsp/jstl/sql" %>
<sql:setDataSource><!-- 方式 1:JDBC 直連 -->
<sql:setDataSource
driver="com.mysql.cj.jdbc.Driver"
url="jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=UTF-8"
user="root" password="123456"
var="ds" scope="session" />
<!-- 方式 2:使用已配置的 DataSource (JNDI) -->
<sql:setDataSource dataSource="jdbc/MyDS" var="ds" scope="application" />
建議放在應用啟動頁或 Filter 中,`scope="session"` 或 `application` 避免重複建立。
<sql:query><sql:query dataSource="${ds}" var="result" scope="page" maxRows="10" startRow="0">
SELECT id, name, price FROM products WHERE category = ?
<sql:param value="${param.cat}" />
</sql:query>
關鍵屬性:`sql` (SQL 字串或標籤體)、`dataSource` (資料源)、`var`/`scope` (結果集變數)、`maxRows` (最大筆數)、`startRow` (起始筆數,0-based)。
| 屬性 | 型別 | 說明 |
|---|---|---|
| rows | java.util.SortedMap[] | 每筆記錄為 Map (欄位名→值),最常用:${row.name} |
| rowsByIndex | Object[][] | 二維陣列,索引存取:${row[0]} |
| columnNames | String[] | 欄位名稱陣列 |
| rowCount | int | 結果集總筆數 (受 maxRows 限制前) |
| limitedByMaxRows | boolean | 是否被 maxRows 截斷 (true=有更多資料未讀取) |
<c:forEach var="row" items="${result.rows}">
<tr>
<td><c:out value="${row.id}"/></td>
<td><c:out value="${row.name}"/></td>
<td><c:out value="${row.price}"/></td>
</tr>
</c:forEach>
<sql:update><sql:update dataSource="${ds}" var="count">
INSERT INTO logs (msg, created) VALUES (?, NOW())
<sql:param value="${param.msg}" />
</sql:update>
<c:out value="${count}"/> 筆受影響
適用 INSERT/UPDATE/DELETE/DDL。回傳影響行數 (`int`)。
<sql:param> / <sql:dateParam><sql:param value="..."/>:通用參數,自動處理型別轉換。<sql:dateParam value="..." type="DATE|TIME|TIMESTAMP"/>:日期時間參數,指定 JDBC 型別。<sql:transaction><sql:transaction dataSource="${ds}">
<sql:update>UPDATE accounts SET balance=balance-100 WHERE id=1</sql:update>
<sql:update>UPDATE accounts SET balance=balance+100 WHERE id=2</sql:update>
</sql:transaction>
標籤體內所有操作在同一交易中,任一失敗全體回滾。注意:巢狀交易不支援。
<!-- 計算 startRow -->
<c:set var="page" value="${param.page gt 0 ? param.page : 1}" />
<c:set var="pageSize" value="10" />
<c:set var="startRow" value="${(page - 1) * pageSize}" />
<sql:query dataSource="${ds}" var="result" maxRows="${pageSize}" startRow="${startRow}">
SELECT * FROM articles ORDER BY created DESC
</sql:query>
<!-- 顯示分頁連結 -->
<c:if test="${startRow gt 0}">
<a href="?page=${page-1}">上一頁</a>
</c:if>
<c:if test="${result.limitedByMaxRows}">
<a href="?page=${page+1}">下一頁</a>
</c:if>
生產環境嚴禁直接在 JSP 使用 SQL 標籤庫!僅建議用於:原型驗證、內部報表、極簡單獨立頁面。
- SQL 注入風險 (雖有 param 標籤,但動態 SQL 拼接仍危險)
- 業務邏輯與表現層耦合,違反 MVC
- 連線池管理、事務邊界、異常處理難以控制
- 無法單元測試、程式碼複用性極低
JSTL SQL 標籤庫展示了「頁面直連資料庫」的極致便利,但也提醒我們分層架構的必要性。理解其運作機制,有助於在遷移至 DAO/Service 層時保留參數綁定、分頁、交易等核心邏輯。