初學者常因「缺少驅動包」而被 JDBC 擋在門外。本文從 JDBC 與 JDBC Driver 的區別講起,逐步拆解連接建立、Statement 三大類型、ResultSet 遊標操作及連接池選型,助你避開踩坑陷阱。
java.sql.*、javax.sql.*),僅聲明 Connection、Statement、ResultSet 等接口,不提供實現。mysql-connector-java、Oracle 的 ojdbc),實現 JDBC 接口。設計初衷:面向接口編程,切換數據庫只需換驅動包與連接串,業務代碼無需修改(OCP 原則)。
mysql-connector-java-8.0.33.jar)-cp 或 CLASSPATH 環境變量WEB-INF/lib 或 Maven/Gradle 依賴管理// JDBC 4.0 之前需顯式加載(現已罕用)
Class.forName("com.mysql.cj.jdbc.Driver");
// JDBC 4.0+ 只需確保 JAR 在 classpath,DriverManager 會自動發現 SPI
String url = "jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC";
String user = "root";
String pwd = "123456";
// 方式 1:URL 包含憑證
Connection conn = DriverManager.getConnection(url);
// 方式 2:分離參數(推薦,可讀性強)
Connection conn = DriverManager.getConnection(url, user, pwd);
// 方式 3:Properties 傳遞更多屬性(字符編碼、連接超時等)
Properties props = new Properties();
props.setProperty("user", user);
props.setProperty("password", pwd);
props.setProperty("characterEncoding", "UTF-8");
Connection conn = DriverManager.getConnection(url, props);
主流實現:HikariCP(Spring Boot 2.x 默認)、Druid、DBCP2、C3P0。
優勢:連接複用、預編譯語句緩存、監控指標、優雅關閉。
| 接口 | 創建方法 | 特點 | 適用場景 |
|---|---|---|---|
| Statement | conn.createStatement() | 靜態 SQL,無參數,易受注入攻擊 | 極簡單、無參查詢、DDL |
| PreparedStatement | conn.prepareStatement(sql) | 預編譯、參數化(?佔位符)、防注入、執行計劃緩存 | 90%+ 業務查詢/更新 |
| CallableStatement | conn.prepareCall("{call proc(?,?)}") | 調用存儲過程/函數 | 複雜邏輯下推數據庫 |
String sql = "SELECT id, name, age FROM user WHERE age > ?";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setInt(1, 18);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) { // 遊標前移
long id = rs.getLong("id"); // 推薦用列名,可讀性強
String name = rs.getString("name");
int age = rs.getInt("age");
// 處理行數據
}
}
}
String sql = "INSERT INTO user(name, age) VALUES (?, ?)";
try (PreparedStatement ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
ps.setString(1, "Alice");
ps.setInt(2, 25);
int rows = ps.executeUpdate(); // 返回影響行數
// 獲取自增主鍵
try (ResultSet keys = ps.getGeneratedKeys()) {
if (keys.next()) System.out.println("新ID: " + keys.getLong(1));
}
}
// 創建可滾動、只讀的 ResultSet
Statement stmt = conn.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE, // 可前後移動
ResultSet.CONCUR_READ_ONLY // 唯讀
);
ResultSet rs = stmt.executeQuery("SELECT * FROM user");
rs.last(); // 跳到最後一行
rs.absolute(5); // 跳到第 5 行
rs.relative(-2); // 相對當前行向前 2 行
Connection、Statement、ResultSet(實現 AutoCloseable)SQLException 並轉換為業務異常,避免棧信息洩露DriverManager,必須使用連接池serverTimezone=UTC)com.mysql.cj.jdbc.DrivercharacterEncoding=UTF-8,表/庫建立時指定 utf8mb4PreparedStatementrs.getInt(1) 而非 0