Java Database Connectivity (JDBC) 是 Java 存取資料庫的標準 API,提供了一組統一的介面讓開發者能以純 Java 程式碼操作各類關聯式資料庫。無論是 MySQL、Oracle、PostgreSQL 還是 SQL Server,只要有對應的 JDBC 驅動程式,即可使用相同的程式設計模式進行連線、查詢、更新與事務控制。
JDBC 代表 Java Database Connectivity,是 Sun 公司(現 Oracle)制定的一套標準介面,屬於 java.sql 與 javax.sql 套件。其核心任務包含:
在撰寫 JDBC 程式前,需確認以下資訊:
不同資料庫廠商的通訊協定、儲存格式各異,若開發者需為每種資料庫撰寫專屬程式碼,成本極高。JDBC 採用「介面標準化、實作由廠商提供」的設計:
Connection、Statement、ResultSet 等)| 介面/類別 | 關鍵方法 | 說明 |
|---|---|---|
Driver | connect(url, props) | 驅動程式介面,建立連線 |
Connection | createStatement()prepareStatement(sql)prepareCall(sql)setAutoCommit(boolean)commit()/rollback() | 資料庫連線物件,管理事務 |
Statement | executeQuery(sql)executeUpdate(sql) | 執行靜態 SQL 陳述式 |
PreparedStatement | setInt(idx, val)setString(idx, val)executeQuery()executeUpdate() | 預編譯 SQL,支援參數綁定,防 SQL 注入 |
CallableStatement | executeQuery() | 呼叫預存程序 |
ResultSet | next()getInt(col)getString(col)getObject(col) | 查詢結果集,游標式逐列讀取 |
import java.sql.*;Class.forName("com.mysql.cj.jdbc.Driver");(JDBC 4.0+ 可略過,自動載入)Connection conn = DriverManager.getConnection(url, user, pass);Statement stmt = conn.createStatement(); 或 PreparedStatementexecuteQuery(),增刪改用 executeUpdate()ResultSet.next() 迴圈搭配 getXxx() 取值ResultSet、Statement、Connection(建議用 try-with-resources)package com.example.jdbc;
import java.sql.*;
public class JdbcDemo {
private static final String URL = "jdbc:mysql://localhost:3306/school?useSSL=false&serverTimezone=UTC";
private static final String USER = "root";
private static final String PASS = "your_password";
public static void main(String[] args) {
String ddl = "CREATE TABLE IF NOT EXISTS student (sid INT PRIMARY KEY, sname VARCHAR(20), age INT)";
String insert = "INSERT INTO student VALUES (?, ?, ?)";
String query = "SELECT sid, sname, age FROM student";
try (Connection conn = DriverManager.getConnection(URL, USER, PASS);
Statement stmt = conn.createStatement()) {
stmt.executeUpdate(ddl); // 建表
try (PreparedStatement ps = conn.prepareStatement(insert)) {
ps.setInt(1, 100); ps.setString(2, "小文"); ps.setInt(3, 18); ps.executeUpdate();
ps.setInt(1, 101); ps.setString(2, "大林"); ps.setInt(3, 25); ps.executeUpdate();
}
try (ResultSet rs = stmt.executeQuery(query)) {
while (rs.next()) {
System.out.printf("sid: %d sname: %s age: %d%n",
rs.getInt("sid"), rs.getString("sname"), rs.getInt("age"));
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
將重複的連線建立、資源關閉邏輯抽取為工具類,並改用 PreparedStatement 防注入、提升效能:
package com.example.util;
import java.sql.*;
public final class DBUtil {
private static final String DRIVER = "com.mysql.cj.jdbc.Driver";
private static final String URL = "jdbc:mysql://localhost:3306/school?useSSL=false&serverTimezone=UTC";
private static final String USER = "root";
private static final String PASS = "your_password";
static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { throw new ExceptionInInitializerError(e); } }
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(URL, USER, PASS);
}
public static void close(AutoCloseable... resources) {
for (AutoCloseable r : resources) {
if (r != null) try { r.close(); } catch (Exception ignored) {}
}
}
}
package com.example.dao;
import com.example.util.DBUtil;
import java.sql.*;
public class StudentDao {
public void createTable() {
String sql = "CREATE TABLE IF NOT EXISTS student (sid INT PRIMARY KEY, sname VARCHAR(20), age INT)";
try (Connection conn = DBUtil.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.executeUpdate();
} catch (SQLException e) { e.printStackTrace(); }
}
public void insert(int id, String name, int age) {
String sql = "INSERT INTO student VALUES (?, ?, ?)";
try (Connection conn = DBUtil.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setInt(1, id); ps.setString(2, name); ps.setInt(3, age);
ps.executeUpdate();
} catch (SQLException e) { e.printStackTrace(); }
}
public void findById(int id) {
String sql = "SELECT sid, sname, age FROM student WHERE sid = ?";
try (Connection conn = DBUtil.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery()) {
ps.setInt(1, id);
while (rs.next()) {
System.out.printf("sid: %d sname: %s age: %d%n",
rs.getInt("sid"), rs.getString("sname"), rs.getInt("age"));
}
} catch (SQLException e) { e.printStackTrace(); }
}
}
| 比較項目 | Statement | PreparedStatement |
|---|---|---|
| SQL 形式 | 靜態字串拼接 | 預編譯,使用 ? 佔位符 |
| 效能 | 每次解析、編譯、優化 | 預編譯一次,多次執行僅傳參數,資料庫端可快取執行計畫 |
| 安全性 | 易遭 SQL 注入攻擊 | 參數化查詢,自動處理跳脫字元,防注入 |
| 建議 | 僅適合極簡單、無參數的一次性查詢 | 生產環境首選 |
SQL 注入範例
Statement拼接:"SELECT * FROM users WHERE name='" + input + "'"
惡意輸入:' OR '1'='1→ 繞過驗證。
PreparedStatement會將參數視為純資料,不會被解析為 SQL 片段。
getConnection(url, user, pass) 取得連線createStatement()、prepareStatement(sql)、setAutoCommit(false)、commit()、rollback()executeQuery(sql) 回傳 ResultSet,executeUpdate(sql) 回傳影響列數next() 移動游標,getString(col)、getInt(col)、getObject(col) 取值總結:JDBC 雖屬基礎 API,但掌握其核心流程、資源管理與 PreparedStatement 最佳實踐,是所有 Java 持久層框架的基石。實務上建議搭配連線池與 Spring JDBC Template 等高階封裝,進一步簡化程式碼並提升穩定度。