本文提供一個生產級可用的 DatabaseUtil 類,封裝了連接獲取、查詢、更新、資源關閉全流程,採用 PreparedStatement 防注入、try-with-resources 思想手動管理資源,適合中小項目直接引入或作為教學參考。
package com.excelExport.util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class DatabaseUtil {
// === 配置常量(建議外置到 properties/yaml)===
private static final String DRIVER = "com.mysql.jdbc.Driver";
private static final String URL = "jdbc:mysql://localhost:3306/tmdest";
private static final String USER = "root";
private static final String PWD = "123456";
// === 線程不安全的實例字段(僅供單線程演示,生產請用連接池)===
private Connection con = null;
private PreparedStatement ps = null;
public ResultSet rs = null; // 暴露給調用者遍歷,調用者負責關閉
/** 獲取數據庫連接 */
public void getConnection() {
try {
Class.forName(DRIVER);
con = DriverManager.getConnection(URL, USER, PWD);
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
/** 查詢操作,返回 ResultSet(調用者必須遍歷後調用 closeAll()) */
public ResultSet getQuery(String sql, String[] params) {
getConnection();
try {
ps = con.prepareStatement(sql);
if (params != null) {
for (int i = 0; i < params.length; i++) {
ps.setString(i + 1, params[i]);
}
}
rs = ps.executeQuery();
} catch (SQLException e) {
e.printStackTrace();
}
return rs; // 注意:此處不關閉資源,交由調用者遍歷完後調用 closeAll()
}
/** 更新操作(INSERT/UPDATE/DELETE),返回影響行數 */
public int getUpdate(String sql, String[] params) {
int row = 0;
getConnection();
try {
ps = con.prepareStatement(sql);
if (params != null) {
for (int i = 0; i < params.length; i++) {
ps.setString(i + 1, params[i]);
}
}
row = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
closeAll(); // 更新類操作可立即釋放
}
return row;
}
/** 關閉所有資源 */
private void closeAll() {
try {
if (rs != null) rs.close();
if (ps != null) ps.close();
if (con != null) con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
| 維度 | 當前實現 | 生產級建議 |
|---|---|---|
| 連接管理 | 每次操作新建連接 | 引入 HikariCP/Druid 連接池,複用連接,配置最大/最小閒置、超時、驗證查詢 |
| 線程安全 | 實例字段共享,非線程安全 | 方法內局部變量持有連接/語句/結果集,或使用 ThreadLocal 綁定事務 |
| 資源關閉 | 手動 closeAll(),易忘調用 | Java 7+ try-with-resources 自動關閉;或封裝 execute(queryConsumer) 模板方法 |
| 參數綁定 | 僅支持 String 類型 | 重載 setObject(int, Object) 支持 Date/Integer/BigDecimal 等 |
| 異常處理 | 打印堆疊返回 null/0 | 拋出自定義異常(DataAccessException),上層統一處理、記錄、告警 |
| 配置外置 | 硬編碼常量 | Spring @Value / @ConfigurationProperties 注入,區分 dev/test/prod 環境 |
public T executeQuery(String sql, Object[] params, ResultSetHandler handler) {
try (Connection con = dataSource.getConnection();
PreparedStatement ps = con.prepareStatement(sql)) {
setParams(ps, params);
try (ResultSet rs = ps.executeQuery()) {
return handler.handle(rs);
}
} catch (SQLException e) {
throw new DataAccessException("Query failed: " + sql, e);
}
}
@FunctionalInterface
public interface ResultSetHandler {
T handle(ResultSet rs) throws SQLException;
}
DatabaseUtil util = new DatabaseUtil();
// 查詢
ResultSet rs = util.getQuery("SELECT * FROM user WHERE name = ?", new String[]{"Tom"});
while (rs.next()) {
System.out.println(rs.getString("email"));
}
util.closeAll(); // 必須調用
// 更新
int affected = util.getUpdate("UPDATE user SET age = ? WHERE name = ?", new String[]{"25", "Tom"});
System.out.println("影響行數: " + affected);
此工具類展示了 JDBC 核心 API 的標準用法,理解其原理後,強烈建議直接使用成熟框架或連接池,專注業務邏輯而非基礎設施重複造輪子。