JDBC 提供三種執行 SQL 的接口,各有適用場景:
| 類型 | 繼承關係 | 核心優勢 | 典型場景 |
|---|---|---|---|
| Statement | 基礎接口 | 簡單、無預編譯 | 靜態 SQL、一次性查詢 |
| PreparedStatement | extends Statement | 預編譯、參數化、防注入 | 重複執行、動態參數 |
| CallableStatement | extends PreparedStatement | 調用存儲過程、支持 OUT 參數 | 存儲過程、複雜業務邏輯 |
public void listStudents() throws SQLException {
// 1. 加載驅動(JDBC 4.0+ 可省略)
Class.forName("jdbc.odbc.JdbcOdbcDriver");
// 2. 建立連接
Connection conn = DriverManager.getConnection(
"jdbc:odbc:StudentDB", "admin", "sa");
// 3. 創建 Statement
Statement stmt = conn.createStatement();
// 4. 執行查詢
ResultSet rs = stmt.executeQuery(
"SELECT StudentID, FirstName, LastName FROM Students");
// 5. 處理結果集
System.out.println("Student Number | First Name | Last Name");
while (rs.next()) {
String id = rs.getString(1);
String first = rs.getString(2);
String last = rs.getString(3);
System.out.printf("%s | %s | %s%n", id, first, last);
}
// 6. 關閉資源(建議使用 try-with-resources)
rs.close();
stmt.close();
conn.close();
}
public void updateStudentName(String first, String last, String id)
throws SQLException, ClassNotFoundException {
Connection conn = DriverManager.getConnection(
"jdbc:odbc:StudentDB", "admin", "sa");
Statement stmt = conn.createStatement();
// 風險:字符串拼接易導致 SQL 注入
String sql = "UPDATE Students SET FirstName = '" + first
+ "', LastName = '" + last
+ "' WHERE StudentNumber = " + id;
int rows = stmt.executeUpdate(sql);
System.out.println("Updated " + rows + " rows");
}
// 類級別連接與預編譯語句(連接池環境下推薦)
private Connection conn;
private PreparedStatement pstmt;
private boolean initialized = false;
private void initConnection() throws SQLException, ClassNotFoundException {
Class.forName("jdbc.odbc.JdbcOdbcDriver");
conn = DriverManager.getConnection("jdbc:odbc:StudentDB", "admin", "sa");
// 1. SQL 預編譯,? 為佔位符
pstmt = conn.prepareStatement(
"SELECT ClassName, Location, DaysAndTimes FROM Classes WHERE ClassName = ?");
initialized = true;
}
public void listOneClass(String className) throws SQLException {
if (!initialized) initConnection();
// 2. 綁定參數(索引從 1 開始)
pstmt.setString(1, className);
// 3. 執行查詢
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
System.out.printf("%s | %s | %s%n",
rs.getString(1), rs.getString(2), rs.getString(3));
}
rs.close();
}
setInt/setDate 等強類型方法public void displayGrade(String studentId, String classId) throws SQLException {
Class.forName("jdbc.odbc.JdbcOdbcDriver");
Connection conn = DriverManager.getConnection(
"jdbc:odbc:studentDB", "admin", "sa");
// 1. 準備調用:{? = call proc_name(?, ?)}
CallableStatement cstmt = conn.prepareCall(
"{? = call getStudentGrade(?, ?)}");
// 2. 註冊 OUT 參數(返回值)
cstmt.registerOutParameter(1, Types.INTEGER);
// 3. 設置 IN 參數
cstmt.setString(2, studentId); // 索引 2 對應第一個 ?
cstmt.setString(3, classId); // 索引 3 對應第二個 ?
// 4. 執行
cstmt.executeUpdate(); // 或 execute()
// 5. 獲取返回值
int grade = cstmt.getInt(1);
System.out.println("The Grade is: " + grade);
cstmt.close();
conn.close();
}
registerOutParameter(index, sqlType) 聲明輸出參數類型// try-with-resources 自動關閉
public void queryWithAutoClose(String className) {
String sql = "SELECT * FROM Classes WHERE ClassName = ?";
try (Connection conn = DriverManager.getConnection(URL, USER, PASS);
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, className);
try (ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
// 處理數據
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
現代項目建議結合連接池(HikariCP/Druid)與 ORM 框架(MyBatis/JPA)使用,直接操作 JDBC 僅在極少量底層優化場景出現。