JDBC 架構核心概念
┌─────────────┐ ┌──────────────────┐ ┌─────────────┐
│ Java 應用程序 │────▶│ JDBC API │────▶│ 數據庫驅動程序 │
│ │ │ (java.sql.*) │ │ (廠商實現) │
└─────────────┘ └──────────────────┘ └──────┬──────┘
│
┌───────▼───────┐
│ 數據庫服務器 │
│ (MySQL/Oracle) │
└───────────────┘
- JDBC API:JDK 提供的標準接口 (
java.sql.*, javax.sql.*)
- 驅動程序:數據庫廠商實現的 JDBC 接口 (如
mysql-connector-j, ojdbc)
- DriverManager:管理驅動註冊、建立連接的服務類
驅動加載的兩種方式
方式一:Class.forName() (推薦,JDBC 4.0+ 可省略)
// JDBC 4.0+ (Java 6+) 會自動通過 SPI 機制加載驅動,無需顯式調用
// 但顯式調用無害,且便於理解
Class.forName("com.mysql.cj.jdbc.Driver"); // MySQL 8.0+
// Class.forName("com.mysql.jdbc.Driver"); // MySQL 5.x (已棄用)
方式二:DriverManager.registerDriver()
Driver driver = new com.mysql.cj.jdbc.Driver();
DriverManager.registerDriver(driver);
// 缺點:會創建兩個 Driver 實例 (一個在靜態塊註冊,一個手動註冊)
建立數據庫連接
String url = "jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC&characterEncoding=UTF-8";
String username = "root";
String password = "123456";
Connection conn = DriverManager.getConnection(url, username, password);
System.out.println("數據庫連接成功: " + conn.getClass().getName());
MySQL URL 參數詳解
| 參數 | 說明 | 推薦值 |
| useSSL | 是否使用 SSL | false (本地開發) |
| serverTimezone | 服務器時區 | UTC 或 Asia/Shanghai |
| characterEncoding | 字符編碼 | UTF-8 |
| allowPublicKeyRetrieval | 允許公鑰獲取 (MySQL 8.0+) | true |
| autoReconnect | 自動重連 | true (生產慎用) |
| failOverReadOnly | 故障轉移只讀 | false |
完整連接測試代碼
public class JdbcConnectionTest {
private static final String URL = "jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC";
private static final String USER = "root";
private static final String PASS = "123456";
public static void main(String[] args) {
Connection conn = null;
try {
// 1. 加載驅動 (JDBC 4.0+ 可省略)
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 建立連接
conn = DriverManager.getConnection(URL, USER, PASS);
System.out.println("✅ 連接成功: " + conn);
// 3. 獲取數據庫元信息
DatabaseMetaData meta = conn.getMetaData();
System.out.println("數據庫產品: " + meta.getDatabaseProductName());
System.out.println("版本: " + meta.getDatabaseProductVersion());
System.out.println("驅動: " + meta.getDriverName());
} catch (ClassNotFoundException e) {
System.err.println("❌ 驅動類未找到,請檢查 Maven 依賴");
e.printStackTrace();
} catch (SQLException e) {
System.err.println("❌ 連接失敗: " + e.getMessage());
e.printStackTrace();
} finally {
// 4. 關閉連接 (必須!)
closeQuietly(conn);
}
}
private static void closeQuietly(AutoCloseable closeable) {
if (closeable != null) {
try { closeable.close(); } catch (Exception ignored) {}
}
}
}
JDBC 核心接口體系
| 接口 | 作用 | 核心方法 |
| Driver | 驅動接口 | connect(), acceptsURL() |
| Connection | 數據庫連接會話 | createStatement(), prepareStatement(), commit(), rollback(), setAutoCommit() |
| Statement | 執行靜態 SQL | execute(), executeQuery(), executeUpdate(), executeBatch() |
| PreparedStatement | 預編譯 SQL (防注入) | setXxx(), executeQuery(), executeUpdate() |
| CallableStatement | 調用存儲過程 | registerOutParameter(), execute() |
| ResultSet | 結果集游標 | next(), getXxx(), findColumn(), getMetaData() |
| ResultSetMetaData | 結果集元數據 | getColumnCount(), getColumnName(), getColumnType() |
Statement vs PreparedStatement
// ❌ Statement: 拼接 SQL,有注入風險,性能差 (每次編譯)
Statement stmt = conn.createStatement();
String sql = "SELECT * FROM user WHERE name = '" + name + "'"; // 危險!
ResultSet rs = stmt.executeQuery(sql);
// ✅ PreparedStatement: 預編譯,參數化,防注入,性能好 (緩存执行计划)
String sql = "SELECT * FROM user WHERE name = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, name); // 參數索引從 1 開始
ResultSet rs = ps.executeQuery();
資源管理:try-with-resources (Java 7+)
// 自動關閉資源,順序:ResultSet → Statement → Connection
public void queryUsers() {
String sql = "SELECT id, name, email FROM user WHERE status = ?";
try (Connection conn = DriverManager.getConnection(URL, USER, PASS);
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setInt(1, 1);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
long id = rs.getLong("id");
String name = rs.getString("name");
String email = rs.getString("email");
System.out.printf("ID: %d, Name: %s, Email: %s%n", id, name, email);
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
事務控制
public void transfer(String fromAccount, String toAccount, double amount) {
Connection conn = null;
try {
conn = DriverManager.getConnection(URL, USER, PASS);
conn.setAutoCommit(false); // 關閉自動提交
// 扣款
updateBalance(conn, fromAccount, -amount);
// 入款
updateBalance(conn, toAccount, amount);
conn.commit(); // 提交事務
} catch (SQLException e) {
if (conn != null) {
try { conn.rollback(); } catch (SQLException ignored) {}
}
throw new RuntimeException("轉賬失敗", e);
} finally {
if (conn != null) {
try { conn.setAutoCommit(true); conn.close(); } catch (SQLException ignored) {}
}
}
}
private void updateBalance(Connection conn, String account, double amount) throws SQLException {
String sql = "UPDATE account SET balance = balance + ? WHERE account_no = ?";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setDouble(1, amount);
ps.setString(2, account);
ps.executeUpdate();
}
}
連接池:生產環境必備
頻繁創建/銷毀 Connection 開銷極大,必須使用連接池:
| 連接池 | 特點 | Maven 坐標 |
| HikariCP | 性能最優、輕量、Spring Boot 2 默認 | com.zaxxer:HikariCP |
| Druid | 阿里出品、監控強、中文文檔好 | com.alibaba:druid |
| DBCP2 | Apache 老牌、穩定 | org.apache.commons:commons-dbcp2 |
| C3P0 | 老牌、配置複雜、較少用 | com.mchange:c3p0 |
HikariCP 核心配置
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb?useSSL=false");
config.setUsername("root");
config.setPassword("123456");
config.setMaximumPoolSize(20);
config.setMinimumIdle(5);
config.setConnectionTimeout(30000);
config.setIdleTimeout(600000);
config.setMaxLifetime(1800000);
config.setPoolName("MyPool");
HikariDataSource ds = new HikariDataSource(config);
// 使用
try (Connection conn = ds.getConnection()) { ... }
常見異常與排查
| 異常 | 原因 | 解決 |
| ClassNotFoundException | 驅動 jar 未導入 | 檢查 Maven 依賴、scope 非 provided |
| Communications link failure | 網絡不通、IP/端口錯、防火牆 | ping/telnet 測試、檢查 bind-address |
| Access denied for user | 用戶名/密碼錯、權限不足 | 檢查用戶、host、GRANT 權限 |
| Unknown database | 數據庫不存在 | 創建數據庫、檢查名稱拼寫 |
| Public Key Retrieval is not allowed | MySQL 8.0+ 缺少參數 | URL 加 allowPublicKeyRetrieval=true |
| Connection reset | 連接超時被服務器斷開 | 配置 autoReconnect、驗證查詢、調整 wait_timeout |
總結
JDBC 是 Java 數據庫操作的基石,核心掌握:驅動加載 (SPI 自動)、DriverManager.getConnection、PreparedStatement 防注入、try-with-resources 資源管理、事務 ACID、連接池 (HikariCP/Druid)。實際開發中建議使用 Spring JDBC Template、MyBatis、JPA 等上層框架,但理解 JDBC 底層原理對排查問題、優化性能至關重要。