掌握 JSP 使用 JSTL SQL 標籤庫進行資料庫操作:環境建置、連線設定、CRUD 完整範例、參數化查詢、中文編碼處理與連線池最佳實務。
mysql-connector-java-5.1.39-bin.jar (或更新版本)Tomcat/lib/ 目錄 (所有 Web 應用共用)WEB-INF/lib/ (僅單一應用)-- 建立資料庫
CREATE DATABASE RUNOOB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 建立資料表
CREATE TABLE `websites` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` CHAR(20) NOT NULL DEFAULT '' COMMENT '站點名稱',
`url` VARCHAR(255) NOT NULL DEFAULT '',
`alexa` INT(11) NOT NULL DEFAULT '0' COMMENT 'Alexa 排名',
`country` CHAR(10) NOT NULL DEFAULT '' COMMENT '國家',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 插入測試資料
INSERT INTO `websites` VALUES
(1, 'Google', 'https://www.google.com/', 1, 'USA'),
(2, '淘寶', 'https://www.taobao.com/', 13, 'CN'),
(3, '菜鳥教程', 'https://www.runoob.com', 5892, ''),
(4, '微博', 'https://weibo.com/', 20, 'CN'),
(5, 'Facebook', 'https://www.facebook.com/', 3, 'USA');

<%@ taglib prefix="sql" uri="https://java.sun.com/jsp/jstl/sql" %>
<%@ taglib prefix="c" uri="https://java.sun.com/jsp/jstl/core" %>
<sql:setDataSource var="ds"
driver="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/RUNOOB?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Taipei"
user="root"
password="123456" />
| 參數 | 值 | 用途 |
|---|---|---|
| useUnicode | true | 使用 Unicode 字元集 |
| characterEncoding | utf-8 | 指定字元編碼,解決中文亂碼 |
| useSSL | false | 關閉 SSL (本地開發環境) |
| serverTimezone | Asia/Taipei | 設定時區,避免時區警告 |
| autoReconnect | true | 自動重連 (生產環境建議) |
<%@ page language="java" contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="sql" uri="https://java.sun.com/jsp/jstl/sql" %>
<%@ taglib prefix="c" uri="https://java.sun.com/jsp/jstl/core" %>
<html><head><title>JSP 資料庫範例</title></head><body>
<sql:setDataSource var="ds" driver="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/RUNOOB?useUnicode=true&characterEncoding=utf-8"
user="root" password="123456"/>
<!-- 具體操作放在此處 -->
<h1>JSP 資料庫範例 - 菜鳥教程</h1>
<table border="1" width="100%">
<tr><th>ID</th><th>站點名</th><th>站點網址</th></tr>
<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.url}"/></td>
</tr>
</c:forEach>
</table>
</body></html>
<sql:query dataSource="${ds}" var="result">
SELECT * FROM websites ORDER BY alexa ASC;
</sql:query>

<sql:update dataSource="${ds}" var="count">
INSERT INTO websites (name, url, alexa, country)
VALUES ('菜鳥教程行動站', 'https://m.runoob.com', 5093, 'CN');
</sql:update>
<!-- 新增後查詢驗證 -->
<sql:query dataSource="${ds}" var="result">
SELECT * FROM websites;
</sql:query>

<sql:update dataSource="${ds}" var="count">
DELETE FROM websites WHERE id = ?
<sql:param value="${11}" />
</sql:update>
<sql:query dataSource="${ds}" var="result">
SELECT * FROM websites;
</sql:query>

<c:set var="siteId" value="3"/>
<sql:update dataSource="${ds}" var="count">
UPDATE websites SET name = 'RUNOOB' WHERE id = ?
<sql:param value="${siteId}" />
</sql:update>
<sql:query dataSource="${ds}" var="result">
SELECT * FROM websites;
</sql:query>

<c:set var="keyword" value="${param.search}"/>
<c:set var="minAlexa" value="${param.minAlexa}"/>
<sql:query dataSource="${ds}" var="result">
SELECT * FROM websites
WHERE name LIKE ?
AND alexa >= ?
ORDER BY alexa ASC
<sql:param value="%${keyword}%" />
<sql:param value="${minAlexa}" />
</sql:query>
<c:set var="pageSize" value="10"/>
<c:set var="pageNum" value="${param.pageNum gt 0 ? param.pageNum : 1}"/>
<c:set var="offset" value="${(pageNum - 1) * pageSize}"/>
<sql:query dataSource="${ds}" var="result" maxRows="${pageSize}" startRow="${offset + 1}">
SELECT * FROM websites ORDER BY id DESC
</sql:query>
<!-- 總筆數查詢 -->
<sql:query dataSource="${ds}" var="countResult">
SELECT COUNT(*) as total FROM websites
</sql:query>
<c:set var="totalRecords" value="${countResult.rows[0].total}"/>
<c:set var="totalPages" value="${(totalRecords + pageSize - 1) / pageSize}"/>
<sql:transaction dataSource="${ds}">
<sql:update var="count1">
UPDATE accounts SET balance = balance - 100 WHERE id = 1
</sql:update>
<sql:update var="count2">
UPDATE accounts SET balance = balance + 100 WHERE id = 2
</sql:update>
</sql:transaction>
<Context>
<Resource name="jdbc/RUNOOB"
auth="Container"
type="javax.sql.DataSource"
maxTotal="20"
maxIdle="10"
maxWaitMillis="10000"
username="root"
password="123456"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/RUNOOB?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Taipei"
validationQuery="SELECT 1"
testOnBorrow="true"
testWhileIdle="true"
timeBetweenEvictionRunsMillis="300000"
minEvictableIdleTimeMillis="600000"
removeAbandonedOnBorrow="true"
removeAbandonedTimeout="60"
logAbandoned="true" />
</Context>
<resource-ref>
<description>MySQL Connection Pool</description>
<res-ref-name>jdbc/RUNOOB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
<sql:setDataSource dataSource="java:comp/env/jdbc/RUNOOB" var="ds"/>
| 參數 | 建議值 | 調整依據 |
|---|---|---|
| maxTotal | 20-50 | 根據伺服器 CPU 核心數、預期並發數 |
| maxIdle | maxTotal 的 50% | 保持熱連線減少建立開銷 |
| maxWaitMillis | 10000-30000 | 避免請求無限等待 |
| validationQuery | SELECT 1 | 輕量驗證連線有效性 |
| testOnBorrow | true | 借用時驗證,確保連線可用 |
| removeAbandonedOnBorrow | true | 回收洩漏連線 |
| 問題 | 原因 | 解決 |
|---|---|---|
| 中文亂碼 | 連線未指定 characterEncoding、資料表非 utf8mb4 | URL 加參數、資料表轉 utf8mb4、JSP pageEncoding=UTF-8 |
| ClassNotFoundException | JDBC 驅動未放入 Tomcat/lib | 複製 jar 至 Tomcat/lib、重啟 Tomcat |
| Communications link failure | MySQL 服務未啟動、port 錯誤、防火牆阻擋 | 啟動 MySQL、確認 3306 port、開放防火牆 |
| Connection refused | 連線池耗盡、MySQL max_connections 限制 | 調大連線池、調大 MySQL max_connections、檢查洩漏 |
| SSL 連線錯誤 | MySQL 8.0+ 預設要求 SSL | URL 加 useSSL=false 或配置 SSL 憑證 |
| 時區警告 | 伺服器時區未設定 | URL 加 serverTimezone=Asia/Taipei |
<sql:param>,防止 SQL Injection