本系列第二部分,詳細實現 SqlParameter 多型別參數封裝類別,以及 getDataTable 核心查詢邏輯:從連線獲取、預編譯、參數綁定、結果集轉換為 DataTable 結構。
package cdu.yas.xykps.util;
import java.sql.Blob;
import java.sql.Date;
public class SqlParameter {
private String type; // 型別標識:"int", "String", "boolean", "Date", "Blob"
private String value; // String 型別值
private int intValue; // int 型別值
private boolean boolValue; // boolean 型別值
private Date dateValue; // Date 型別值
private Blob blobValue; // Blob 型別值
// 五種建構子,對應五種支援型別
public SqlParameter(String type, String value) { this.type = type; this.value = value; }
public SqlParameter(String type, int intValue) { this.type = type; this.intValue = intValue; }
public SqlParameter(String type, boolean boolValue) { this.type = type; this.boolValue = boolValue; }
public SqlParameter(String type, Date dateValue) { this.type = type; this.dateValue = dateValue; }
public SqlParameter(String type, Blob blobValue) { this.type = type; this.blobValue = blobValue; }
// Getter/Setter 略
public String getType() { return type; }
public String getValue() { return value; }
public int getIntValue() { return intValue; }
public boolean getBoolValue() { return boolValue; }
public Date getDateValue() { return dateValue; }
public Blob getBlobValue() { return blobValue; }
// ... setters
}
設計意圖:以單一類別封裝「型別+值」,避免 Object 擴大型別擦除風險,運行期以字串型別標識分發至對應 JDBC setXxx 方法。
public DataTable getDataTable(String sql, SqlParameter[] params) {
Connection conn = null;
PreparedStatement ps = null;
DataTable table = null;
try {
conn = DB.createConn(); // 獲取連線
ps = DB.prepare(conn, sql); // 預編譯
addSqlParameter(ps, params); // 綁定參數
ResultSet rs = ps.executeQuery(); // 執行查詢
ResultSetMetaData rsmd = rs.getMetaData(); // 獲取元資料
int columnCount = rsmd.getColumnCount();
List rows = new ArrayList<>(); // 所有行集合
while (rs.next()) { // 逐行讀取
List columns = new ArrayList<>();
for (int i = 1; i <= columnCount; i++) {
String colName = rsmd.getColumnName(i);
Object value = rs.getObject(colName);
columns.add(new DataColumn(colName, value)); // 單元格
}
rows.add(new DataRow(columns)); // 封裝行
}
table = new DataTable(rows); // 封裝表
rs.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DB.close(ps);
DB.close(conn); // 釋放資源
}
return table;
}
// 簡化版結構
public class DataTable {
private List rows;
public DataTable(List rows) { this.rows = rows; }
public List getRows() { return rows; }
// 靜態工具方法
public static void outTable(DataTable t) { ... }
public static DataTable joinTable(DataTable t1, DataTable t2, String key1, String key2) { ... }
}
public class DataRow {
private List columns;
public DataRow(List columns) { this.columns = columns; }
public List getColumns() { return columns; }
}
public class DataColumn {
private String name;
private Object value;
public DataColumn(String name, Object value) { this.name = name; this.value = value; }
public String getName() { return name; }
public Object getValue() { return value; }
}
public class DB {
public static Connection createConn() { /* DriverManager.getConnection(...) */ }
public static PreparedStatement prepare(Connection conn, String sql) {
return conn.prepareStatement(sql);
}
public static void close(AutoCloseable resource) {
if (resource != null) try { resource.close(); } catch (Exception ignored) {}
}
}
| 決策點 | 選擇 | 理由 |
|---|---|---|
| 參數型別標識 | 字串 ("int", "String") | 簡單、可序列化、易擴充 |
| 結果集映射 | rs.getObject(columnName) | 通用、自動處理型別轉換 |
| 資源管理 | finally 區塊手動關閉 | JDK 6- 沒有 try-with-resources,確保不洩漏 |
| 異常處理 | catch 列印堆疊,返回 null | 簡單演示;生產環境建議拋出自定義異常或使用 Optional |
展示 addSqlParameter 具體實現細節,以及業務層實際調用代碼與多表連接演示。