在 Java Web 應用中,透過 Tomcat 內建的 JNDI 資料源管理資料庫連線池,能集中設定、提升效能、簡化應用程式碼。本文以 SQL Server 2008 + Tomcat 6 為例,詳細說明 JDBC 驅動部署、context.xml、web.xml 設定、測試 JSP 範例,並說明常見坑點。
sqljdbc4.jar (對應 Java 6/7/8) 或 mssql-jdbc-x.x.x.jre8.jar (新版)將驅動 JAR 放入 Tomcat 共用類別路徑:
# Linux/macOS
cp sqljdbc4.jar $CATALINA_HOME/lib/
# Windows
copy sqljdbc4.jar %CATALINA_HOME%\lib\
注意:請勿放在應用程式 WEB-INF/lib,否則各應用會載入各自副本,無法共用連線池且易造成類別衝突。
編輯 $CATALINA_HOME/conf/context.xml,在 <Context> 內新增 <Resource>:
<Resource name="jdbc/TestDB"
auth="Container"
type="javax.sql.DataSource"
maxActive="100"
maxIdle="30"
maxWait="10000"
username="sa"
password="YourStrongPassword"
driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver"
url="jdbc:sqlserver://localhost:1433;databaseName=CuLeg;encrypt=true;trustServerCertificate=true"
validationQuery="SELECT 1"
testOnBorrow="true"
testWhileIdle="true"
timeBetweenEvictionRunsMillis="30000"
minEvictableIdleTimeMillis="60000" />
| 屬性 | 建議值 | 說明 |
|---|---|---|
| maxActive | 100 | 最大活躍連線數,依應用併發與 DB 最大連線數調整。 |
| maxIdle | 30 | 最大閒置連線數,避免頻繁建立/銷毀開銷。 |
| maxWait | 10000 | 取得連線最長等待毫秒數,超過拋出例外。 |
| validationQuery | SELECT 1 | 借用連線前驗證有效性,防止斷線存活問題。 |
| testOnBorrow | true | 每次借用時驗證。 |
| encrypt / trustServerCertificate | true | SQL Server 2008+ 預設要求加密連線,驅動 6.0+ 需額外指定。 |
編輯應用程式 WEB-INF/web.xml,宣告資源參照:
<resource-ref>
<description>DB Connection</description>
<res-ref-name>jdbc/TestDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.sql.*, javax.sql.*, javax.naming.*" %>
<%@ page session="false" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JNDI DataSource Test</title>
</head>
<body>
<h1>Hello, test JNDI!</h1>
<%
DataSource ds = null;
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
InitialContext ctx = new InitialContext();
ds = (DataSource) ctx.lookup("java:comp/env/jdbc/TestDB");
conn = ds.getConnection();
stmt = conn.createStatement();
rs = stmt.executeQuery("SELECT * FROM account");
while (rs.next()) {
out.println("第一欄: " + rs.getString(1) + "<br>");
out.println("第二欄: " + rs.getString(2) + "<br>");
}
out.println("<br>JDBC 操作成功!");
} catch (Exception e) {
out.println("<font color='red'>錯誤: " + e.getMessage() + "</font>");
e.printStackTrace();
} finally {
if (rs != null) try { rs.close(); } catch (Exception ignore) {}
if (stmt != null) try { stmt.close(); } catch (Exception ignore) {}
if (conn != null) try { conn.close(); } catch (Exception ignore) {}
}
%>
</body>
</html>
$CATALINA_HOME/lib 或檔名錯誤。context.xml 的 name 與 web.xml res-ref-name 不一致,或 JNDI 路徑錯誤 (需為 java:comp/env/jdbc/TestDB)。encrypt=true;trustServerCertificate=true (測試環境) 或匯入正式憑證。finally 區塊關閉 ResultSet、Statement、Connection,歸還連線池。context.xml,可使用 Tomcat PasswordEncoder 或外部化設定 (如 Spring Cloud Config、HashiCorp Vault)。numActive、numIdle、maxWaitTime,透過 JMX 或 Prometheus 收集。validationQuery 清理失效連線,避免防火牆斷開長閒置連線。完成上述設定後,應用程式即可透過標準 JNDI 查詢取得受管理的資料庫連線,享受連線池帶來的效能與穩定性優勢。記住:驅動放 lib、資源定義在 context.xml、參照在 web.xml、程式碼 lookup("java:comp/env/..."),四步走不易出錯。