核心實現邏輯
public static List<Customer> queryCustomers() {
Connection conn = new ConnectionUtil().openConnection();
try {
Statement stmt = conn.createStatement();
String sql = "SELECT id, name, email FROM customertbl";
ResultSet rs = stmt.executeQuery(sql);
List<Customer> list = new ArrayList<>();
while (rs.next()) {
Customer c = new Customer();
c.setId(rs.getInt("id")); // 推薦用列名,避免 SELECT * 次序變更風險
c.setName(rs.getString("name"));
c.setEmail(rs.getString("email"));
list.add(c);
}
return list;
} catch (SQLException e) {
throw new RuntimeException("查詢失敗", e);
} finally {
closeQuietly(conn);
}
}
關鍵最佳實踐
| 維度 | 建議 | 理由 |
| 資源管理 | try-with-resources (JDK 7+) 或 finally 關閉 | 防止連接/Statement/ResultSet 洩漏 |
| 列訪問 | 優先 getXxx("columnName") | 不依賴 SELECT 列順序,重構安全 |
| 泛型 | List<T> 而非原始 List | 編譯期類型安全,消除強轉 |
| 異常處理 | 包裝為非受檢異常或統一異常體系 | 調用者無需捕獲 SQLException |
| 空值處理 | rs.getString() 返回 null,基本類型用包裝類或判斷 wasNull() | 避免基本類型無法表示 NULL |
通用映射工具封裝(進階)
public interface RowMapper<T> {
T mapRow(ResultSet rs, int rowNum) throws SQLException;
}
public class BeanRowMapper<T> implements RowMapper<T> {
private final Class<T> clazz;
public BeanRowMapper(Class<T> clazz) { this.clazz = clazz; }
public T mapRow(ResultSet rs, int rowNum) throws SQLException {
T obj = clazz.getDeclaredConstructor().newInstance();
ResultSetMetaData meta = rs.getMetaData();
for (int i = 1; i <= meta.getColumnCount(); i++) {
String col = meta.getColumnLabel(i);
Object val = rs.getObject(i);
// 反射設置字段或 setter,需處理類型轉換、駝峰映射
BeanUtils.setProperty(obj, col, val);
}
return obj;
}
}
// 使用
List<Customer> list = query("SELECT * FROM customertbl", new BeanRowMapper<>(Customer.class));
生產環境建議
- 使用成熟 ORM (MyBatis, JDBI, Spring JdbcTemplate) 替代手寫映射
- 連接池 (HikariCP) 管理連接生命週期
- 大結果集採用流式處理或分頁,避免全量載入 OOM
- 列名與字段名映射規約化(下劃線↔駝峰)