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

JDBC 通用查詢封裝:利用 ResultSetMetaData 實現與資料表結構解耦

     編輯:Java 相關知識

問題背景

傳統 JDBC 查詢需針對每張資料表寫對應的 ResultSet 解析邏輯,資料庫結構變更時維護成本高。本文示範利用 ResultSetMetaData 動態取得欄位資訊,搭配 List<Object[]> 通用資料結構,實現單一方法查詢任意資料表。

核心設計

  1. ResultSetMetaData.getColumnCount() 取得欄位數
  2. ResultSetMetaData.getColumnName(i) / getColumnTypeName(i) 取得欄位名稱與型別
  3. 每一列以 Object[] 陣列儲存,整個結果集為 List<Object[]>
  4. 呼叫端透過迭代器或索引存取,必要時依型別轉換

完整實作類別

import java.math.BigDecimal;
import java.sql.*;
import java.util.*;

public class GenericJdbcDao {

    private final String url;
    private final String username;
    private final String password;

    public GenericJdbcDao(String url, String username, String password) {
        this.url = url;
        this.username = username;
        this.password = password;
        try {
            // Oracle 範例,依實際驅動調整
            Class.forName("oracle.jdbc.driver.OracleDriver");
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("JDBC 驅動載入失敗", e);
        }
    }

    private Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url, username, password);
    }

    /**
     * 通用查詢,回傳 List<Object[]>,每個 Object[] 代表一列資料
     */
    public List query(String sql) {
        List result = new ArrayList<>();
        try (Connection conn = getConnection();
             Statement stmt = conn.createStatement();
             ResultSet rs = stmt.executeQuery(sql)) {

            ResultSetMetaData meta = rs.getMetaData();
            int columnCount = meta.getColumnCount();

            // 可選:記錄欄位名稱與型別供呼叫端參考
            // List columnNames = new ArrayList<>();
            // for (int i = 1; i <= columnCount; i++) columnNames.add(meta.getColumnName(i));

            while (rs.next()) {
                Object[] row = new Object[columnCount];
                for (int i = 1; i <= columnCount; i++) {
                    row[i - 1] = rs.getObject(i); // getObject 自動對應 Java 型別
                }
                result.add(row);
            }
        } catch (SQLException e) {
            throw new RuntimeException("查詢執行失敗: " + sql, e);
        }
        return result;
    }

    /**
     * 通用更新 (INSERT/UPDATE/DELETE)
     */
    public int update(String sql) {
        try (Connection conn = getConnection();
             Statement stmt = conn.createStatement()) {
            return stmt.executeUpdate(sql);
        } catch (SQLException e) {
            throw new RuntimeException("更新執行失敗: " + sql, e);
        }
    }

    // 測試主程式
    public static void main(String[] args) {
        GenericJdbcDao dao = new GenericJdbcDao(
            "jdbc:oracle:thin:@localhost:1521:orcl", "scott", "tiger");

        List rows = dao.query("SELECT empno, ename, sal, hiredate FROM emp");
        for (Object[] row : rows) {
            System.out.printf("工號:%s 姓名:%s 薪資:%s 到職:%s%n",
                row[0], row[1], row[2], row[3]);
        }
    }
}

進階擴充建議

  • 連線池:生產環境必須整合 HikariCP / Druid / DBCP,避免頻繁建立連線開銷。
  • PreparedStatement:支援參數化查詢防 SQL 注入,query(String sql, Object... params)。
  • 型別安全映射:搭配 RowMapper<T> 介面將 Object[] 映射為領域物件(類 Spring JdbcTemplate)。
  • 分頁支援:Oracle ROWNUM、MySQL LIMIT、SQL Server OFFSET/FETCH 方言抽象。
  • 資源管理:採用 try-with-resources (JDK 7+) 確保連線/Statement/ResultSet 正確關閉。

適用場景與限制

優點缺點/注意
單一方法支援任意查詢,減少樣板碼失去編譯期型別檢查,執行期才發現型別不符
適合動態報表、匯出、通用查詢介面getObject() 對大型資料型別 (BLOB/CLOB) 效能較差,需分開處理
快速原型開發、資料庫管理工具不適合高頻核心業務交易,建議改用 ORM 或強型別 DAO

總結

ResultSetMetaData 提供資料庫結構的運行期反射能力,配合通用集合結構可快速構建動態查詢功能。但生產核心業務仍建議採用強型別映射 (JPA/Hibernate/MyBatis/JdbcTemplate) 以獲得編譯期安全與效能優化。

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