在使用 JDBC 操作資料庫時,常見痛點是:每當資料表結構變更或需查詢不同資料表,皆須重寫結果集(ResultSet)的遍歷邏輯。本文示範如何利用 ResultSetMetaData 取得資料表結構資訊,搭配 Object[] 陣列與 Iterator 迭代器,建立一套與資料表結構無關的通用 JDBC 存取類別,大幅提升程式碼重用性。
rs.getMetaData() 取得 ResultSetMetaData 物件,可獲取欄位數、欄位名稱、型別等資訊。Object[] 陣列,逐筆將 ResultSet 每欄值存入陣列,再將陣列加入 List。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 連線池,即可構建輕量級的資料存取層框架,適合中小型專案快速開發。