Class.forName("com.mysql.cj.jdbc.Driver") (JDBC 4.0+ 可省略,SPI 自動載入)DriverManager.getConnection(url, user, pwd)PreparedStatement 防注入、預編譯優化ps.setInt(1, id)、ps.setString(2, name)executeQuery()/executeUpdate()while(rs.next()) { rs.getInt(1); }try-with-resources 自動關閉 (JDK 7+)public class DBHelper implements AutoCloseable {
private static final String URL = "jdbc:mysql://localhost:3306/mybatis?useSSL=false&serverTimezone=UTC";
private static final String USER = "root";
private static final String PASS = "123456";
private final Connection conn;
private final PreparedStatement ps;
public DBHelper(String sql) throws SQLException {
this.conn = DriverManager.getConnection(URL, USER, PASS);
this.ps = conn.prepareStatement(sql);
}
public PreparedStatement getStatement() { return ps; }
public ResultSet executeQuery() throws SQLException { return ps.executeQuery(); }
public int executeUpdate() throws SQLException { return ps.executeUpdate(); }
@Override
public void close() {
try { if (ps != null) ps.close(); } catch (SQLException ignore) {}
try { if (conn != null) conn.close(); } catch (SQLException ignore) {}
}
}
// 使用範例
try (DBHelper db = new DBHelper("SELECT * FROM user WHERE age > ?")) {
db.getStatement().setInt(1, 18);
try (ResultSet rs = db.executeQuery()) {
while (rs.next()) {
System.out.printf("ID:%d Name:%s Age:%d%n",
rs.getInt("id"), rs.getString("name"), rs.getInt("age"));
}
}
}
生產環境嚴禁逐請求建立連線。主流選擇:
# HikariCP 核心配置 (application.yml)
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
hikari:
maximum-pool-size: 20
minimum-idle: 5
idle-timeout: 300000
connection-timeout: 30000
max-lifetime: 1800000
connection-test-query: SELECT 1
| 問題 | 原因 | 解決 |
|---|---|---|
| 連線洩漏 | 未在 finally 關閉資源 | try-with-resources / 連線池自動回收 |
| SQL 注入 | 字串拼接 SQL | PreparedStatement 參數化 |
| 事務未提交 | 自動提交關閉後忘記 commit | conn.setAutoCommit(false) + 統一 commit/rollback |
| 時區錯誤 | MySQL 8.0+ 預設 UTC | URL 加 serverTimezone=Asia/Taipei |
| 大資料量 OOM | 一次性 fetch 所有行 | 分頁查詢 / 流式讀取 setFetchSize() |