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

JSP 數據庫訪問優化:基於標簽庫封裝 JDBC 實現高性能組件化開發

     編輯:關於JSP

前言

將 JDBC 代碼直接寫在 JSP 中 (Model 1) 會導致業務邏輯與視圖耦合、難以復用、事務管理混亂。本文介紹基於 **JSP 標簽庫 + JavaBean + DataSource** 的組件化方案,實現 SQL 邏輯與視圖分離、連接池復用、聲明式調用,顯著提升開發效率與運行性能。

架構對比

模式結構優點缺點
Model 1 (Page-centric)JSP 直連 JDBC簡單、直觀耦合度高、無復用、事務難控、性能差
Model 2 (MVC)Servlet(Controller) + JavaBean(Model) + JSP(View)分層清晰、可測試、事務集中需寫 Controller、轉發代碼
Taglib 組件化 (本文方案)自定義標簽 + Bean + DataSource**JSP 內聲明式調用、零 Java 代碼、標簽復用、連接池透明**標簽類開發門檻稍高

核心實現步驟

1. 定義標簽處理類 (繼承 BodyTagSupport)

package com.myactions;

import javax.servlet.jsp.tagext.*;
import javax.servlet.jsp.*;
import javax.sql.DataSource;
import javax.naming.*;
import java.sql.*;
import java.util.*;

public class QueryTag extends BodyTagSupport {
    private String sql;           // SQL 語句,支持參數占位符 ?
    private String dataSource;    // JNDI 名稱
    private String var;           // 導出變量名 (List)
    
    // setter 方法對應標簽屬性
    public void setSql(String sql) { this.sql = sql; }
    public void setDataSource(String ds) { this.dataSource = ds; }
    public void setVar(String var) { this.var = var; }

    @Override
    public int doStartTag() throws JspException {
        // 1. 獲取 DataSource (首次查找後可緩存到 application 域)
        DataSource ds = lookupDataSource();
        List> rows = new ArrayList<>();
        
        try (Connection conn = ds.getConnection();
             PreparedStatement ps = conn.prepareStatement(sql)) {
            
            // 2. 參數綁定:從 bodyContent 或請求參數獲取 (簡化示例)
            // 實際可解析 body 內  子標簽
            
            try (ResultSet rs = ps.executeQuery()) {
                ResultSetMetaData meta = rs.getMetaData();
                int cols = meta.getColumnCount();
                while (rs.next()) {
                    Map row = new HashMap<>();
                    for (int i = 1; i <= cols; i++) {
                        row.put(meta.getColumnLabel(i), rs.getObject(i));
                    }
                    rows.add(row);
                }
            }
        } catch (Exception e) {
            throw new JspException("Query failed: " + sql, e);
        }
        
        // 3. 導出到 pageContext 供 EL/JSTL 使用
        pageContext.setAttribute(var, rows, PageContext.PAGE_SCOPE);
        return SKIP_BODY; // 不處理標簽體
    }

    private DataSource lookupDataSource() {
        try {
            Context ctx = new InitialContext();
            return (DataSource) ctx.lookup("java:comp/env/" + dataSource);
        } catch (NamingException e) {
            throw new RuntimeException(e);
        }
    }
}

2. TLD 文件 myactions.tld

<?xml version="1.0" encoding="UTF-8"?>
<taglib xmlns="https://java.sun.com/xml/ns/javaee" version="2.1">
    <tlib-version>1.0</tlib-version>
    <short-name>myactions</short-name>
    <uri>https://example.com/myactions</uri>
    <tag>
        <name>query</name>
        <tag-class>com.myactions.QueryTag</tag-class>
        <body-content>empty</body-content>
        <attribute>
            <name>sql</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
        <attribute>
            <name>dataSource</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
        <attribute>
            <name>var</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>
</taglib>

3. JSP 頁面聲明式調用

<%@ taglib prefix="act" uri="https://example.com/myactions" %>
<%@ taglib prefix="c" uri="https://java.sun.com/jsp/jstl/core" %>

<act:query dataSource="jdbc/MyDS" 
           sql="SELECT id, name, email FROM users WHERE status = ?" 
           var="userList"/>

<table>
    <c:forEach items="${userList}" var="u">
        <tr>
            <td>${u.id}</td>
            <td>${u.name}</td>
            <td>${u.email}</td>
        </tr>
    </c:forEach>
</table>

4. web.xml 配置 DataSource (Tomcat 為例)

<resource-ref>
    <res-ref-name>jdbc/MyDS</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
</resource-ref>

Tomcat context.xml 見前文「技巧 1」。

進階擴展:參數綁定、更新操作、事務控制

參數子標簽

<act:query ... var="list">
    <act:param value="${param.keyword}"/>   <!-- 第 1 個 ? -->
    <act:param value="1"/>                  <-- 第 2 個 ? -->
</act:query>

標簽類中解析 getBodyContent() 或實現 Tag 介面的子標簽處理器。

更新標簽 <act:update>

public class UpdateTag extends TagSupport {
    // 類似 QueryTag,執行 ps.executeUpdate(),返回影響行數
    // 支援批量:addBatch()/executeBatch()
}

事務邊界標簽 (進階)

<act:transaction dataSource="jdbc/MyDS">
    <act:update sql="INSERT INTO orders ..."/>
    <act:update sql="UPDATE inventory SET qty = qty - ? WHERE id = ?"/>
</act:transaction>

實現需在 doStartTag 獲取連接關閉自動提交,doEndTag 提交/回滾,連接存入 pageContext 供子標簽共用。

性能優化要點

  1. DataSource 緩存:標簽類靜態 Map 緩存 JNDI 查找結果,避免每請求查找。
  2. PreparedStatement 緩存:連接池層面開啟 poolPreparedStatements=true (Druid/HikariCP 支援)。
  3. 結果集流式處理:大數據量避免全量載入 List,改用 ResultSet 迭代輸出 (需自定義迭代標簽)。
  4. 標簽池化:容器自動池化標簽處理器實例,確保 release() 重置字段。

總結

標簽庫封裝 JDBC 將 **SQL 邏輯內聚到標簽實現類**,JSP 頁面回歸純視圖模板,**零 Java 代碼、聲明式數據綁定、連接池透明復用**。這是從 Model 1 邁向組件化架構的關鍵一步,也是早期 JSP 專案重構的低成本高收益路徑。

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