身為深耕 .NET 兩年餘的開發者(ADO.NET、多執行緒、Remoting 等),初次接觸 Java 時難免帶有偏見:語法冗長、配置繁瑣、IDE 不如 Visual Studio 直覺。但經過 C++ 的洗禮後,逐漸明白「工具服務於業務」的本質。Java 雖在開發體驗上不如 .NET 流暢,但在功能豐富度、生態成熟度與成本優勢上佔據高端市場。本文分享如何將 .NET 時代總結的資料存取簡化模式,移植到 Java 平台,實現「寫更少代碼,做更多事」。
參考 ADO.NET 的 IDbCommand 模式,定義 IExecutor 介面,屏蔽底層 JDBC 細節:
package queen.data;
public interface IExecutor {
String getConnectionString();
void setConnectionString(String value) throws Exception;
int getTimeout();
void setTimeout(int value);
QueryResult executeQuery(String statement) throws Exception; // 查詢結果集
Object executeScale(String statement) throws Exception; // 單值查詢
int executeNonQuery(String statement) throws Exception; // 增刪改
}
JDBC 原生 ResultSet 需手動關閉 Connection、Statement、ResultSet 三個資源,極易洩漏。設計 QueryResult 統一持有三者引用,提供單一 close() 方法:
public final class QueryResult {
private Connection _connection;
private Statement _statement;
private ResultSet _resultSet;
public QueryResult(Connection conn, Statement stmt, ResultSet rs) {
_connection = conn; _statement = stmt; _resultSet = rs;
}
public ResultSet getResultSet() { return _resultSet; }
public void close() throws SQLException {
_resultSet.close(); _statement.close(); _connection.close();
}
}
封裝驅動載入、連線參數管理、三大執行方法,支援屬性檔讀取配置:
public final class DbExecutor implements IExecutor {
static { Class.forName("com.mysql.jdbc.Driver"); } // 靜態塊載入驅動
private String _connectionString, _userName, _password;
private int _timeout;
// 重載設定連線參數
public void setConnection(String url, String user, String pwd) { ... }
public void setConnectionFromProperties(String file, String urlKey,
String userKey, String pwdKey) throws IOException { ... }
// 執行增刪改
public int executeNonQuery(String sql) throws Exception {
Connection con = null; Statement stm = null;
try {
con = DriverManager.getConnection(_connectionString, _userName, _password);
stm = con.createStatement();
return stm.executeUpdate(sql);
} finally { closeQuietly(stm); closeQuietly(con); }
}
// 執行查詢回傳 QueryResult
public QueryResult executeQuery(String sql) throws Exception {
Connection con = DriverManager.getConnection(_connectionString, _userName, _password);
Statement stm = con.createStatement();
return new QueryResult(con, stm, stm.executeQuery(sql));
}
// 單值查詢(自動關閉資源)
public Object executeScale(String sql) throws Exception { ... }
}
public static void main(String[] args) throws Exception {
DbExecutor db = new DbExecutor();
db.setConnection("jdbc:mysql://host:3306/db?useUnicode=true&characterEncoding=utf8",
"user", "pass");
// 查詢多筆
QueryResult r = db.executeQuery("SELECT Name FROM Users LIMIT 10");
ResultSet rs = r.getResultSet();
while (rs.next()) System.out.println(rs.getString(1));
r.close(); // 一鍵釋放所有資源
// 查詢單值
int maxAge = (Integer) db.executeScale("SELECT MAX(Age) FROM Users");
}
| 痛點 | 解決方案 |
|---|---|
| JDBC 三大物件繁瑣 | 介面統一、工廠模式封裝建立邏輯 |
| 資源關閉易遺漏 | QueryResult 聚合三資源,單一 close() |
| 參數硬編碼 | 支援 Properties 檔外部化配置 |
| 異常處理分散 | 統一拋出 Exception,上層集中處理 |
此簡化版未支援參數化查詢、連線池、事務管理等進階功能,實際生產建議整合 HikariCP、Spring JDBC 或 MyBatis。但核心思想——「以最小代價屏蔽 JDBC 樣板代碼,讓業務邏輯回歸純粹」——在任何框架下都值得堅持。技術棧會變,工程師對「簡潔、可維護、資源安全」的追求不變。