首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

JDBC PreparedStatement 預編譯機制深度解析與通用封裝實戰

     編輯:Java 程式設計入門

為什麼用 PreparedStatement?

優勢說明
效能提升SQL 預編譯,同一條語句多次執行僅編譯一次,批量處理極大降低開銷
防注入參數化查詢,數據與代碼分離,徹底阻斷 SQL 注入
可讀性佔位符 ? 替代字串拼接,邏輯清晰
型別安全setInt/setString/setDate 等強類型綁定
Blob/Clob 支援流式讀寫大對象,Statement 難以勝任

一次性查詢用 Statement 亦可,但習慣統一用 PreparedStatement 更安全。

核心 API 速查


// 1. 獲取預編譯對象
PreparedStatement ps = conn.prepareStatement("SELECT * FROM user WHERE age > ? AND name LIKE ?");

// 2. 綁定參數 (索引從 1 開始)
ps.setInt(1, 18);
ps.setString(2, "Zhang%");

// 3. 執行
ResultSet rs = ps.executeQuery();     // 查詢
int count = ps.executeUpdate();       // 增刪改
boolean hasResult = ps.execute();     // 通用

// 4. 批量處理
for (User u : users) {
    ps.setString(1, u.getName());
    ps.setInt(2, u.getAge());
    ps.addBatch();
}
int[] results = ps.executeBatch(); // 返回各條影響行數

通用封裝:從固定參數到動態參數數組

基礎版:固定兩參數查詢


public ResultSet queryByRange(String sql, String min, String max) {
    getConnection();
    try {
        ps = conn.prepareStatement(sql);
        ps.setString(1, min);
        ps.setString(2, max);
        return ps.executeQuery();
    } catch (SQLException e) { e.printStackTrace(); }
    return null;
}

進階版:動態參數數組查詢


public ResultSet query(String sql, Object... params) {
    getConnection();
    try {
        ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
        for (int i = 0; i < params.length; i++) {
            ps.setObject(i + 1, params[i]); // setObject 自動適配類型
        }
        return ps.executeQuery();
    } catch (SQLException e) { e.printStackTrace(); }
    return null;
}

// 調用
query("SELECT * FROM t_user WHERE birth_year BETWEEN ? AND ?", "1992", "1992");
query("SELECT * FROM t_user WHERE id BETWEEN ? AND ?", 100, 200);

通用增刪改封裝


public int executeUpdate(String sql, Object... params) {
    int count = 0;
    getConnection();
    try {
        ps = conn.prepareStatement(sql);
        if (params != null) {
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i + 1, params[i]);
            }
        }
        count = ps.executeUpdate();
    } catch (SQLException e) { e.printStackTrace(); }
    finally { close(); }
    return count;
}

// 批量新增
for (int i = 0; i < 10; i++) {
    executeUpdate("INSERT INTO jdbc_test(username, password) VALUES(?,?)", "user" + i, "pwd" + i);
}

// 範圍刪除
executeUpdate("DELETE FROM t_user WHERE id BETWEEN ? AND ?", 1010, 1030);

ResultSet 轉二維數組/列表 (供 JTable 等 UI 綁定)


public Object[][] toArray(ResultSet rs) {
    try {
        rs.last();
        int rows = rs.getRow();
        ResultSetMetaData meta = rs.getMetaData();
        int cols = meta.getColumnCount();
        rs.beforeFirst();

        Object[][] data = new Object[rows][cols];
        int r = 0;
        while (rs.next()) {
            for (int c = 0; c < cols; c++) {
                data[r][c] = rs.getObject(c + 1);
            }
            r++;
        }
        return data;
    } catch (SQLException e) { e.printStackTrace(); return new Object[0][0]; }
}

避坑指南

  • setObject 雖通用,但某些驅動對時間類型支援不佳,優先用專用 setDate/setTimestamp
  • 批量執行後記得 clearBatch() 避免內存洩漏
  • ResultSet.TYPE_SCROLL_INSENSITIVE 允許前後移動光標,便於獲取行數,但消耗更大
  • 長連接場景必須配置連接池,勿頻繁開關連接
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved