封裝 JDBC 重複樣板代碼:連線獲取、PreparedStatement 參數綁定、ResultSet 釋放、事務提交/回滾,讓具體 DAO 專注 SQL 與業務邏輯。
package dao;
import java.sql.*;
public class BaseDao {
// 資料庫配置(實務建議外部化至 properties)
private static final String DRIVER = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
private static final String URL = "jdbc:sqlserver://localhost:1433;DatabaseName=BookManage";
private static final String USER = "sa";
private static final String PASS = "";
protected Connection conn;
protected PreparedStatement ps;
protected ResultSet rs;
// 獲取連線(含驅動載入)
public Connection getCon() {
try {
Class.forName(DRIVER);
conn = DriverManager.getConnection(URL, USER, PASS);
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
return conn;
}
// 統一資源釋放(逆序關閉)
public void closeAll(ResultSet rs, PreparedStatement ps, Connection conn) {
try {
if (rs != null) rs.close();
if (ps != null) ps.close();
if (conn != null) conn.close();
} catch (SQLException e) { e.printStackTrace(); }
}
// 通用增刪改:支援可變參數綁定
public int executeUpdate(String sql, Object... params) {
int rows = 0;
try {
ps = getCon().prepareStatement(sql);
if (params != null) {
for (int i = 0; i < params.length; i++) {
ps.setObject(i + 1, params[i]); // 索引從 1 開始
}
}
rows = ps.executeUpdate();
} catch (SQLException e) { e.printStackTrace(); }
finally { closeAll(rs, ps, conn); }
return rows;
}
// 可擴充:通用查詢模板方法(回調 RowMapper)
// public List executeQuery(String sql, RowMapper mapper, Object... params) { ... }
}
public class BookDao extends BaseDao {
public int add(Book book) {
String sql = "INSERT INTO t_book(name, author, price) VALUES(?,?,?)";
return executeUpdate(sql, book.getName(), book.getAuthor(), book.getPrice());
}
public int deleteById(int id) {
return executeUpdate("DELETE FROM t_book WHERE id=?", id);
}
}
| 亮點 | 待改進 |
|---|---|
| 消除樣板代碼,子類極度精簡 | 連線資訊硬編碼,建議注入 DataSource |
| 可變參數靈活綁定,防 SQL 注入 | 無連線池,高併發效能差 |
| finally 保證資源釋放 | 缺乏查詢模板(RowMapper/ResultSetHandler) |
| 異常吞沒僅列印,建議拋出自定義異常 | 事務控制粒度粗(單語句自動提交) |