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

JDBC 數據庫核心操作全攻略:連接、CRUD、事務、批量、可滾動結果集

     編輯:Java 相關知識

前言:JDBC 是 Java 持久層的基石

無論你用 MyBatis、Hibernate、JPA 還是 Spring Data,底層皆是 JDBC。掌握原生 API 能讓你排查疑難雜癥、優化性能、理解框架原理。本文系統梳理 標準流程、四大核心對象、進階特性(事務/批量/可滾動結果集),附最佳實踐代碼模板。

一、 標準操作流程:四步走

  1. 獲取連接:Connection
  2. 執行 SQL:Statement / PreparedStatement
  3. 處理結果:ResultSet (查詢) / int 影響行數 (更新)
  4. 釋放資源:逆序關閉 ResultSet → Statement → Connection

二、 獲取連接:兩種方式

1. DriverManager(獨立應用、測試)

String url = "jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC";
String user = "root", pwd = "123456";
Class.forName("com.mysql.cj.jdbc.Driver"); // JDBC 4.0+ 可省略
Connection conn = DriverManager.getConnection(url, user, pwd);

2. JNDI + DataSource(Web 容器、生產環境)


// web.xml / context.xml 配置資源引用
Context ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup("java:comp/env/jdbc/myds");
Connection conn = ds.getConnection(); // 從連接池獲取,用完 close() 歸還池中

優勢:連接池管理、事務集成、安全隔離、配置與代碼分離。

三、 執行 SQL:Statement vs PreparedStatement

特性StatementPreparedStatement
SQL 注入防護無,需手工轉義參數化佔位符 ?,驅動層自動處理
性能(重複執行)每次編譯預編譯一次,多次綁定參數執行
批量更新addBatch(sql)setXxx() → addBatch() → executeBatch()
Blob/Clob 處理困難原生支持 setBlob/setClob

原則:生產代碼 99% 用 PreparedStatement。僅在 DDL、一次性腳本用 Statement。

// PreparedStatement 查詢範例
String sql = "SELECT id, name, age FROM users WHERE age > ? AND status = ?";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
    ps.setInt(1, 18);
    ps.setString(2, "active");
    try (ResultSet rs = ps.executeQuery()) {
        while (rs.next()) {
            System.out.println(rs.getInt("id") + " " + rs.getString("name"));
        }
    }
}

// 更新範例
String upd = "UPDATE users SET age = ? WHERE id = ?";
try (PreparedStatement ps = conn.prepareStatement(upd)) {
    ps.setInt(1, 20);
    ps.setLong(2, 1001L);
    int rows = ps.executeUpdate();
    System.out.println("影響行數: " + rows);
}

四、 進階特性

1. 可滾動、可更新結果集


// 創建可滾動、只讀結果集
Statement stmt = conn.createStatement(
    ResultSet.TYPE_SCROLL_INSENSITIVE,  // 不感知底層數據變化
    ResultSet.CONCUR_READ_ONLY
);
ResultSet rs = stmt.executeQuery("SELECT * FROM large_table");
rs.absolute(9000); // 直接跳轉到第 9000 行
rs.beforeFirst();  // 回到起始位置
rs.afterLast();    // 移到末尾

類型選擇:TYPE_SCROLL_INSENSITIVE 性能較好;TYPE_SCROLL_SENSITIVE 能看到別的事務提交的修改,開銷大。

2. 批量更新:大幅提升寫入吞吐


// 模式 A: Statement 批量執行不同 SQL
Statement stmt = conn.createStatement();
stmt.addBatch("INSERT INTO log VALUES (1, 'msg1')");
stmt.addBatch("UPDATE config SET val='x' WHERE k='y'");
stmt.addBatch("DELETE FROM temp WHERE id < 100");
int[] counts = stmt.executeBatch(); // 返回每條影響行數

// 模式 B: PreparedStatement 同一 SQL 多組參數(更常用)
String sql = "INSERT INTO orders (user_id, amount) VALUES (?, ?)";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
    for (Order o : orderList) {
        ps.setLong(1, o.getUserId());
        ps.setBigDecimal(2, o.getAmount());
        ps.addBatch();
        if (++count % 1000 == 0) { // 分批提交,防止內存溢出
            ps.executeBatch();
            conn.commit();
        }
    }
    ps.executeBatch(); // 剩餘
    conn.commit();
}

3. 事務處理:ACID 保障

conn.setAutoCommit(false); // 關鍵:關閉自動提交
try {
    // 業務邏輯:多條 SQL 組成原子操作
    transfer(fromAccount, toAccount, amount); // 內部用同一 conn 執行多條 update
    updateLedger(fromAccount, -amount);
    updateLedger(toAccount, amount);

    conn.commit(); // 全部成功提交
} catch (Exception e) {
    conn.rollback(); // 任一失敗回滾
    throw e;
} finally {
    conn.setAutoCommit(true); // 恢復默認行為
}

關鍵規則:同一事務內,每條 SQL 執行前必須關閉上一條的 Statement/PreparedStatement(或使用 try-with-resources 自動關閉),否則可能導致鎖競爭、游標泄漏。

五、 資源關閉最佳實踐:Try-with-resources (Java 7+)

public void queryUsers(int minAge) {
    String sql = "SELECT id, name FROM users WHERE age > ?";
    // 自動關閉 conn、ps、rs,即使拋異常
    try (Connection conn = ds.getConnection();
         PreparedStatement ps = conn.prepareStatement(sql)) {
        ps.setInt(1, minAge);
        try (ResultSet rs = ps.executeQuery()) {
            while (rs.next()) {
                // 處理數據
            }
        }
    } catch (SQLException e) {
        log.error("查詢失敗", e);
        throw new RuntimeException(e);
    }
}

六、 避坑清單

  1. 忘記關閉資源 → 連接池耗盡、內存洩漏。
  2. 拼接 SQL 導致注入 → 永遠用 ? 佔位符。
  3. 大結果集未流式處理 → OOM。MySQL 需設置 setFetchSize(Integer.MIN_VALUE) 啟用流式讀取。
  4. 事務邊界不清 → 業務邏輯與事務控制耦合。建議用 @Transactional (Spring) 或模板方法隔離。
  5. 批量提交無分批 → 內存飆升。每 500-1000 條 executeBatch() + commit()。

總結

JDBC 雖「原始」,卻是所有上層框架的基石。熟練掌握 連接獲取、PreparedStatement 參數化、ResultSet 流式讀取、事務邊界控制、批量寫入優化,能讓你在性能調優、疑難排查、框架選型時游刃有餘。下一步建議深入:連接池原理 (HikariCP)、數據庫鎖機制、分布式事務 (Seata/AT 模式)。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved