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

JDBC 通用資料存取:利用 ResultSetMetaData 實現程式碼重用

     編輯:Java 相關知識

在使用 JDBC 操作資料庫時,常見痛點是:每當資料表結構變更或需查詢不同資料表,皆須重寫結果集(ResultSet)的遍歷邏輯。本文示範如何利用 ResultSetMetaData 取得資料表結構資訊,搭配 Object[] 陣列與 Iterator 迭代器,建立一套與資料表結構無關的通用 JDBC 存取類別,大幅提升程式碼重用性。

核心設計思路

  1. 取得中繼資料:呼叫 rs.getMetaData() 取得 ResultSetMetaData 物件,可獲取欄位數、欄位名稱、型別等資訊。
  2. 動態映射欄位:根據欄位數建立 Object[] 陣列,逐筆將 ResultSet 每欄值存入陣列,再將陣列加入 List。
  3. 統一介面回傳:查詢方法回傳 List<Object[]>,呼叫端透過 Iterator 遍歷,再依欄位順序轉型取值。

完整實作範例

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

public class UniversalJdbc {
    private String url = "jdbc:oracle:thin:@localhost:1521:orcl";
    private String user = "scott";
    private String password = "tiger";
    private Connection conn = null;
    private Statement stmt = null;
    private ResultSet rs = null;

    public UniversalJdbc() {
        try { Class.forName("oracle.jdbc.driver.OracleDriver"); }
        catch (ClassNotFoundException e) { e.printStackTrace(); }
    }

    public Connection getConnection() {
        try { conn = DriverManager.getConnection(url, user, password); }
        catch (SQLException e) { e.printStackTrace(); }
        return conn;
    }

    public void close(ResultSet rs, Statement stmt, Connection conn) {
        try { if (rs != null) rs.close(); } catch (SQLException e) { e.printStackTrace(); }
        try { if (stmt != null) stmt.close(); } catch (SQLException e) { e.printStackTrace(); }
        try { if (conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); }
    }

    /** 通用查詢:回傳 List<Object[]>,每筆資料為 Object 陣列 */
    public List<Object[]> query(String sql) {
        List<Object[]> list = new ArrayList<>();
        conn = getConnection();
        try {
            stmt = conn.createStatement();
            rs = stmt.executeQuery(sql);
            ResultSetMetaData meta = rs.getMetaData();
            int colCount = meta.getColumnCount();
            while (rs.next()) {
                Object[] row = new Object[colCount];
                for (int i = 0; i < colCount; i++) {
                    row[i] = rs.getObject(i + 1);
                }
                list.add(row);
            }
        } catch (SQLException e) { e.printStackTrace(); }
        finally { close(rs, stmt, conn); }
        return list;
    }

    /** 通用更新:INSERT/UPDATE/DELETE */
    public void update(String sql) {
        conn = getConnection();
        try {
            stmt = conn.createStatement();
            stmt.executeUpdate(sql);
        } catch (SQLException e) { e.printStackTrace(); }
        finally { close(null, stmt, conn); }
    }

    public static void main(String[] args) {
        UniversalJdbc jdbc = new UniversalJdbc();
        String sql = "SELECT * FROM users";
        List<Object[]> list = jdbc.query(sql);
        Iterator<Object[]> it = list.iterator();
        while (it.hasNext()) {
            Object[] row = it.next();
            // 假設第一欄為數值型主鍵
            int id = ((BigDecimal) row[0]).intValue();
            System.out.println("ID: " + id);
            // 其餘欄位依序取用 row[1], row[2]...
        }
    }
}

關鍵優勢與注意事項

  • 零硬編碼:無需為每個資料表撰寫專屬的 ResultSet 解析邏輯。
  • 彈性擴充:可進一步封裝為 Map<String, Object> 以欄位名稱存取,或結合反射自動映射至 POJO。
  • 型別轉換:呼叫端需自行處理型別轉換(如 BigDecimal 轉 int),建議搭配 ResultSetMetaData.getColumnType() 輔助判斷。
  • 資源管理:務必在 finally 區塊關閉連線、陳述式、結果集,避免洩漏。
  • 效能考量:通用查詢較具體映射略慢,大量資料場景建議改用連線池與批次處理。

延伸應用

此模式可擴充為簡易的 DAO 基底類別,搭配 PreparedStatement 防止 SQL 注入,並整合 DataSource 連線池,即可構建輕量級的資料存取層框架,適合中小型專案快速開發。

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