在 Java 開發中,操作 MySQL 資料庫是最基礎也最核心的技能之一。雖然現代專案多使用 Spring Data JPA、MyBatis 等 ORM 框架,但理解原生 JDBC 的運作機制對於排查效能問題、處理特殊場景至關重要。本文將從驅動安裝、連線建立、CRUD 操作、資源管理到最佳實踐,系統性地講解 Java 操作 MySQL 的完整流程。
mysql-connector-j 8.x 版本(Maven 坐標:com.mysql:mysql-connector-j:8.0.33)以下範例展示了標準的 JDBC 操作模式,採用 Try-with-resources 自動關閉資源,使用 PreparedStatement 防止 SQL 注入,並採用連線池概念(此處為示範直接建立連線)。
import java.sql.*;
import java.util.Properties;
public class MySqlDemo {
// 資料庫連線參數(實際專案請放入設定檔)
private static final String URL = "jdbc:mysql://localhost:3306/your_db?useSSL=false&serverTimezone=UTC&characterEncoding=utf-8";
private static final String USER = "root";
private static final String PASSWORD = "your_password";
public static void main(String[] args) {
// 1. 建立連線並執行查詢
try (Connection conn = DriverManager.getConnection(URL, USER, PASSWORD)) {
System.out.println("資料庫連線成功!");
// 2. 查詢示範
queryUsers(conn);
// 3. 新增示範
insertUser(conn, "Alice", "alice@example.com");
// 4. 更新示範
updateUserEmail(conn, 1, "new_email@example.com");
// 5. 刪除示範
deleteUser(conn, 2);
} catch (SQLException e) {
e.printStackTrace();
}
}
// 查詢所有使用者
private static void queryUsers(Connection conn) throws SQLException {
String sql = "SELECT id, username, email FROM users";
try (PreparedStatement pstmt = conn.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery()) {
System.out.println("--- 使用者列表 ---");
while (rs.next()) {
int id = rs.getInt("id");
String username = rs.getString("username");
String email = rs.getString("email");
System.out.printf("ID: %d, Username: %s, Email: %s%n", id, username, email);
}
}
}
// 新增使用者
private static void insertUser(Connection conn, String username, String email) throws SQLException {
String sql = "INSERT INTO users (username, email) VALUES (?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
pstmt.setString(1, username);
pstmt.setString(2, email);
int affectedRows = pstmt.executeUpdate();
if (affectedRows > 0) {
try (ResultSet generatedKeys = pstmt.getGeneratedKeys()) {
if (generatedKeys.next()) {
System.out.println("新增成功,自增 ID: " + generatedKeys.getLong(1));
}
}
}
}
}
// 更新使用者 Email
private static void updateUserEmail(Connection conn, int id, String newEmail) throws SQLException {
String sql = "UPDATE users SET email = ? WHERE id = ?";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, newEmail);
pstmt.setInt(2, id);
int rows = pstmt.executeUpdate();
System.out.println("更新 " + rows + " 筆資料");
}
}
// 刪除使用者
private static void deleteUser(Connection conn, int id) throws SQLException {
String sql = "DELETE FROM users WHERE id = ?";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, id);
int rows = pstmt.executeUpdate();
System.out.println("刪除 " + rows + " 筆資料");
}
}
}
從 JDBC 4.0 (Java 6) 開始,不再需要手動呼叫 Class.forName("com.mysql.cj.jdbc.Driver")。只要將驅動 JAR 放在 Classpath 中,DriverManager 會透過 SPI 機制自動發現並註冊驅動。
PreparedStatement 使用參數佔位符 (?),能有效防止 SQL 注入攻擊。Java 7 引入的語法糖,確保 Connection、Statement、ResultSet 等實作 AutoCloseable 的物件在區塊結束時自動關閉,即使發生異常也不會洩漏資源。
實際生產環境嚴禁頻繁建立/關閉物理連線。請務必使用成熟的連線池框架:
| 問題 | 原因 | 解決方案 |
|---|---|---|
| 中文亂碼 | 字元編碼不一致 | URL 加上 characterEncoding=utf-8,資料庫、表、欄位皆設為 utf8mb4 |
| 時區錯誤 | JDBC 8.0+ 預設使用伺服器時區 | URL 加上 serverTimezone=Asia/Taipei 或 UTC |
| SSL 連線失敗 | MySQL 8.0 預設要求 SSL | 開發環境 URL 加 useSSL=false;生產環境請配置憑證 |
| 驅動預設不允許傳輸公鑰 | URL 加 allowPublicKeyRetrieval=true (需搭配 useSSL=false) |
掌握原生 JDBC 是 Java 後端工程師的基本功。雖然日常開發多依賴 ORM 框架,但理解連線建立、SQL 預編譯、事務控制、資源釋放等底層機制,能幫助你寫出更高效、更穩健的資料存取層代碼。記住:永遠使用 PreparedStatement、永遠使用連線池、永遠妥善關閉資源。