首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> SQL Server 資料庫 >> 關於SqlServer

Tomcat 連線池整合 SQL Server 2000 完整教學:從 JDBC 驅動到 JNDI 實戰

     編輯:關於SqlServer

為什麼 Web 應用需要連線池?

HTTP 無狀態特性導致每次請求都建立新資料庫連線,造成:

  • 頻繁建立/銷毀 TCP 連線與驗證交握開銷大
  • 資料庫連線數暴增,易觸發「最大連線數」限制
  • 連線洩漏風險高(例外未正確關閉)

Tomcat 內建 DBCP (Database Connection Pool),透過 JNDI 統一管理 DataSource,實現連線重用、預先建立、驗證與自動回收。

一、環境準備與 JDBC 驅動安裝

1. Tomcat 版本選擇

  • 建議:Tomcat 6.0.x (zip 解壓版),避免 Windows Service 版註冊表/權限問題
  • 下載:https://tomcat.apache.org/download-60.cgi

2. SQL Server 2000 JDBC 驅動 (關鍵步驟!)

必須使用 Microsoft 官方舊版驅動,新版驅動不支援 SQL 2000:

  • 下載:https://www.microsoft.com/downloads/details.aspx?FamilyID=99b21b65-6d79-4a58-87f5-5a5e4d5b5b5b (搜尋 "SQL Server 2000 JDBC Driver")
  • 安裝後取得三個核心 JAR:
    msbase.jar、mssqlserver.jar、msutil.jar
  • 複製至 $CATALINA_HOME/lib/ 目錄 (Tomcat 6/7/8/9 通用)
⚠ 常見坑點:若下載的驅動安裝後缺少上述三個 JAR,即為錯誤版本。SQL 2000 僅相容 Microsoft 2005 年釋出的 1.2 版驅動。

二、配置 JNDI DataSource (context.xml)

編輯 $CATALINA_HOME/conf/context.xml,在 <Context> 內新增:

<Resource name="jdbc/DBTest"
          auth="Container"
          type="javax.sql.DataSource"
          maxActive="100"
          maxIdle="30"
          maxWait="10000"
          username="sa"
          password="your_password"
          driverClassName="com.microsoft.jdbc.sqlserver.SQLServerDriver"
          url="jdbc:microsoft:sqlserver://192.168.1.100:1433;DatabaseName=Plat_01358"
          validationQuery="SELECT 1"
          testOnBorrow="true"
          testWhileIdle="true"
          timeBetweenEvictionRunsMillis="60000"
          minEvictableIdleTimeMillis="300000" />

關鍵屬性解析

屬性建議值說明
namejdbc/您的名稱JNDI 查找鍵,程式碼中對應
maxActive100最大活躍連線數,視 DB 最大連線數調整
maxIdle30閒置保留連線數,減少建立開銷
maxWait10000取得連線最長等待毫秒數,防止無限阻塞
validationQuerySELECT 1連線有效性檢查 SQL,SQL Server 專用
testOnBorrowtrue借用時驗證,確保連線存活
testWhileIdletrue空閒檢測器定期驗證

三、Java 程式碼中使用 DataSource

import javax.naming.*;
import javax.sql.DataSource;
import java.sql.*;

public class UserDao {
    public void queryUser(String userNum) {
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        
        try {
            // 1. 取得 JNDI 初始上下文
            Context initCtx = new InitialContext();
            
            // 2. 查找 DataSource (java:/comp/env/ + resource name)
            DataSource ds = (DataSource) initCtx.lookup("java:/comp/env/jdbc/DBTest");
            
            // 3. 從連線池取得連線
            conn = ds.getConnection();
            
            // 4. 使用 PreparedStatement 防注入
            String sql = "SELECT PS_NUM, PS_NAME FROM Person WHERE PS_NUM = ?";
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, userNum);
            
            // 5. 執行查詢
            rs = pstmt.executeQuery();
            while (rs.next()) {
                System.out.println(rs.getString("PS_NUM") + " " + rs.getString("PS_NAME"));
            }
        } catch (NamingException | SQLException e) {
            e.printStackTrace();
            throw new RuntimeException("資料庫操作失敗", e);
        } finally {
            // 6. 逆序關閉資源 (連線歸還池中,非實際關閉)
            closeQuietly(rs);
            closeQuietly(pstmt);
            closeQuietly(conn);
        }
    }
    
    private void closeQuietly(AutoCloseable resource) {
        if (resource != null) try { resource.close(); } catch (Exception ignore) {}
    }
}

四、完整 JSP 範例 (含錯誤處理)

<%@ page import="javax.naming.*,javax.sql.*,java.sql.*" %>
<%
    String userNum = request.getParameter("num");
    if (userNum == null || userNum.trim().isEmpty()) {
        out.println("請提供 num 參數");
        return;
    }
    
    Connection conn = null;
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    
    try {
        Context envCtx = new InitialContext();
        DataSource ds = (DataSource) envCtx.lookup("java:/comp/env/jdbc/DBTest");
        conn = ds.getConnection();
        
        pstmt = conn.prepareStatement("SELECT PS_NUM, PS_NAME FROM Person WHERE PS_NUM = ?");
        pstmt.setString(1, userNum);
        rs = pstmt.executeQuery();
        
        if (!rs.isBeforeFirst()) {
            out.println("查無資料");
        } else {
            while (rs.next()) {
                out.println("編號: " + rs.getString("PS_NUM") + ", 姓名: " + rs.getString("PS_NAME") + "<br>");
            }
        }
    } catch (Exception e) {
        out.println("系統錯誤:" + e.getMessage());
        e.printStackTrace(); // 實際專案請用 Logger
    } finally {
        if (rs != null) try { rs.close(); } catch (Exception ignore) {}
        if (pstmt != null) try { pstmt.close(); } catch (Exception ignore) {}
        if (conn != null) try { conn.close(); } catch (Exception ignore) {}
    }
%>

五、常見問題排查清單

現象可能原因解決方案
NameNotFoundException: jdbc/DBTestJNDI 名稱錯誤確認 context.xml name 屬性與 lookup 字串一致 (含 java:/comp/env/ 前綴)
ClassNotFoundException: SQLServerDriverJAR 未放入 lib三個 JAR 必須在 $CATALINA_HOME/lib,重啟 Tomcat
Connection refused / Network errorIP/埠/防火牆telnet IP 1433 測試連通性;SQL Server 設定允許 TCP/IP
中文亂碼編碼不一致JSP pageEncoding=UTF-8、DB 欄位用 NVARCHAR、JDBC URL 加 ;useUnicode=true;characterEncoding=UTF-8
連線洩漏 (池耗盡)未在 finally 關閉強制使用 try-with-resources 或 closeQuietly 模式
Stale connection (通訊鏈路斷裂)DB 端斷開閒置連線啟用 validationQuery + testOnBorrow + testWhileIdle

六、生產環境調優建議

  1. 容量規劃:maxActive = (CPU 核心數 x 2) + 有效磁碟數,但不超過 SQL Server max connections 設定
  2. 監控指標:啟用 JMX 監控 NumActive、NumIdle、MaxWaitTime
  3. 驗證優化:validationQuery 使用極輕量 SQL (SELECT 1),避免查詢實際表
  4. 棄用連線處理:removeAbandoned="true" removeAbandonedTimeout="60" logAbandoned="true" 偵測未歸還連線
  5. 遷移建議:新專案建議使用 HikariCP (獨立庫) 取代 Tomcat 內建 DBCP,效能與穩定性更優

完成上述配置,即可在 Tomcat 應用中獲得高可用、高效能的 SQL Server 2000 資料庫連線管理能力。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved