在 MVC 架構的控制器組件中,頻繁進行資料庫連線、增刪改查。將 JDBC 樣板碼封裝為 DatabaseBean,提供:
package com.newland.nlie.highway.pub_com;
import java.sql.*;
import javax.naming.Context;
import javax.sql.DataSource;
public class DatabaseBean {
private Connection conn = null;
private Statement stmt = null;
/** 私有建構子,建議搭配單例或依賴注入使用 */
public DatabaseBean() {}
/** 透過 JNDI 取得 DataSource 建立連線 */
private void buildConnection() throws Exception {
try {
Context ctx = InitBean.getInitContext(); // 專案自有工具類
// JBoss 環境
Object obj = ctx.lookup("java:OracleDS");
// WebSphere 環境
// Object obj = ctx.lookup("Pub_DataSource");
DataSource ds = (DataSource) obj;
this.conn = ds.getConnection();
this.stmt = conn.createStatement();
} catch (Exception ex) {
throw new Exception("資料庫連線失敗", ex);
}
}
/** 取得 PreparedStatement */
public PreparedStatement getPreparedStatement(String sql) throws Exception {
if (conn == null) buildConnection();
return conn.prepareStatement(sql);
}
/** 執行更新 */
public void executeUpdate(String sql) throws Exception {
if (conn == null) buildConnection();
stmt.executeUpdate(sql);
}
/** 執行查詢 */
public ResultSet executeQuery(String sql) throws Exception {
if (conn == null) buildConnection();
return stmt.executeQuery(sql);
}
/** 設定自動提交 */
public void setAutoCommit(boolean autoCommit) throws Exception {
if (conn == null) buildConnection();
conn.setAutoCommit(autoCommit);
}
/** 回滾 */
public boolean rollback() {
try {
if (conn != null && !conn.getAutoCommit()) {
conn.rollback();
}
return true;
} catch (SQLException ex) {
return false;
}
}
/** 關閉資源 */
public void close() {
try {
if (stmt != null) stmt.close();
if (conn != null && !conn.isClosed()) conn.close();
} catch (SQLException ex) {
ex.printStackTrace();
}
}
}
public class UserController {
public void addUser(User user) {
DatabaseBean db = new DatabaseBean();
try {
db.setAutoCommit(false);
String sql = "INSERT INTO users(id, name, pwd) VALUES (?, ?, ?)";
PreparedStatement ps = db.getPreparedStatement(sql);
ps.setString(1, user.getId());
ps.setString(2, user.getName());
ps.setString(3, user.getPwd());
ps.executeUpdate();
db.setAutoCommit(true); // 隱含 commit
} catch (Exception e) {
db.rollback();
throw new RuntimeException(e);
} finally {
db.close();
}
}
public List<User> listUsers() {
DatabaseBean db = new DatabaseBean();
List<User> list = new ArrayList<>();
try {
ResultSet rs = db.executeQuery("SELECT * FROM users");
while (rs.next()) {
User u = new User();
u.setId(rs.getString("id"));
u.setName(rs.getString("name"));
list.add(u);
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
db.close();
}
return list;
}
}
| 問題 | 改進方向 |
|---|---|
| 每次操作 new DatabaseBean,連線池利用率低 | 改為 ThreadLocal 綁定連線、或 Spring JdbcTemplate/DataSourceTransactionManager 托管 |
| ResultSet 游標在 db.close() 後失效 | 查詢結果在 finally 前轉為 List/DTO 返回,避免外層持有關閉的 ResultSet |
| SQL 硬編碼分散 | 採用 MyBatis/JPA 將 SQL 外部化至 XML/Annotation |
| 例外處理吞噬堆疊 | 拋出自訂 DaoException 包裝原始異常,保留堆疊 |
| 無連線洩漏偵測 | 整合 HikariCP + leakDetectionThreshold |
DatabaseBean 示範了 JDBC 基礎封裝的核心要素:連線池、預編譯、事務邊界、資源確定性釋放。實務專案建議直接採用成熟框架,但理解其內部運作有助於排查連線洩漏、死鎖、SQL 注入等經典問題。