在 Tomcat 中配置 JNDI 資料源,讓應用透過 JNDI 查找 DataSource,實現連接池管理、與程式碼解耦。
# 複製 MySQL Connector/J JAR 到 Tomcat 共用庫目錄
cp mysql-connector-java-8.x.x.jar $CATALINA_HOME/lib/
注意: Tomcat 9/10 使用 $CATALINA_HOME/lib,舊版可能是 common/lib。請勿放在應用 WEB-INF/lib,否則類載入器隔離會導致 ClassNotFoundException。
編輯 $CATALINA_HOME/conf/context.xml,在 <Context> 節點下新增:
<Resource
name="jdbc/MysqlConnectionPoolTest"
auth="Container"
type="javax.sql.DataSource"
driverClassName="com.mysql.cj.jdbc.Driver"
url="jdbc:mysql://127.0.0.1:3306/test?useSSL=false&serverTimezone=UTC&characterEncoding=utf8"
username="root"
password="your_password"
maxTotal="20"
maxIdle="10"
maxWaitMillis="-1"
validationQuery="SELECT 1"
testOnBorrow="true"
testWhileIdle="true"
timeBetweenEvictionRunsMillis="30000"
/>
關鍵屬性說明:
auth="Container":由容器管理認證(標準值)maxTotal:最大連接數(舊版 maxActive)maxWaitMillis:取得連接最長等待毫秒,-1 表無限等待validationQuery + testOnBorrow:借用連接前驗證有效性,防斷連在應用 WEB-INF/web.xml 的 <web-app> 下新增:
<resource-ref>
<description>MySQL Connection Pool</description>
<res-ref-name>jdbc/MysqlConnectionPoolTest</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
import javax.naming.*;
import javax.sql.DataSource;
import java.sql.*;
public class DBUtil {
private static DataSource dataSource;
static {
try {
Context initCtx = new InitialContext();
// 關鍵:java:comp/env/ 前綴
dataSource = (DataSource) initCtx.lookup("java:comp/env/jdbc/MysqlConnectionPoolTest");
} catch (NamingException e) {
throw new RuntimeException("JNDI DataSource lookup failed", e);
}
}
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
// 使用範例
public void queryUsers() {
String sql = "SELECT id, name FROM users";
try (Connection conn = getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
System.out.println(rs.getInt("id") + ": " + rs.getString("name"));
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
| 陷阱 | 症狀 | 解決 |
|---|---|---|
| JNDI 名稱錯誤 | NameNotFoundException | 確認 java:comp/env/ 前綴 + context.xml 中 name 一致 |
| 驅動類名錯誤 | ClassNotFoundException | MySQL 8.0+ 用 com.mysql.cj.jdbc.Driver,5.x 用 com.mysql.jdbc.Driver |
| URL 參數缺失 | 時區錯誤、SSL 報錯、中文亂碼 | 加 useSSL=false&serverTimezone=UTC&characterEncoding=utf8 |
| 連接洩漏 | 池耗盡、應用掛起 | 必須用 try-with-resources 關閉 Connection/Statement/ResultSet |
| 防火牆/網路 | Communications link failure | 檢查 3306 埠、MySQL bind-address、安全組 |
maxTotal = (CPU 核心數 × 2) + 有效磁碟數,典型 20-100testOnBorrow + validationQuery,或用 testWhileIdle + timeBetweenEvictionRunsMillis 定期清理org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactoryjakarta.*,資料源工廠類相應調整JNDI 資料源實現了資源配置與業務代碼分離,是傳統 Java EE 應用的標準做法。