首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> Oracle 資料庫 >> Oracle 資料庫基礎

Oracle 應用開發三大經典坑:JDBC 驅動載入、大物件存儲、可捲動結果集

     編輯:Oracle 資料庫基礎

問題一:JDBC 驅動載入的三種姿勢

方式代碼優點缺點/適用場景
1. Class.forNameClass.forName("oracle.jdbc.OracleDriver")最常見、規格標準MS JVM (IE Applet) 無法正確執行靜態初始化區塊註冊驅動
2. 系統屬性檔jdbc.drivers=oracle.jdbc.OracleDriver 在屬性檔可同時載入多驅動、切換資料庫無需改代碼需確保屬性檔被 JVM 讀取 (啟動參數 -D 或 System.getProperties().load())
3. 手動註冊 (最可靠)DriverManager.registerDriver(new oracle.jdbc.OracleDriver())任何環境 (含 Applet、OSGi、模組化) 均生效靈活度較低、硬編碼類名

建議:一般 Web 應用用方式 1;Applet/特殊容器用方式 3;需多資料庫熱切換用方式 2。

問題二:大物件 (LOB) 存儲 —— BLOB vs LONG RAW

誤區:以為 PreparedStatement.setBinaryStream 可直接寫入 BLOB,實則拋出 IOException 而非 SQLException,JDBC 2.0 標準不支援直接流寫入 BLOB。

方案 A:使用 LONG RAW (已棄用,僅相容舊系統)


CREATE TABLE tb_file (name VARCHAR2(20), detail LONG RAW);

File file = new File("image.gif");
try (InputStream fin = new FileInputStream(file);
     PreparedStatement ps = conn.prepareStatement("INSERT INTO tb_file VALUES (?, ?)")) {
    ps.setString(1, file.getName());
    ps.setBinaryStream(2, fin, (int)file.length());
    ps.executeUpdate();
}

讀取:ResultSet.getBinaryStream("detail") 直接回傳 InputStream。

方案 B:標準 BLOB 流程 (Oracle 專有擴充)


CREATE TABLE tb_file (name VARCHAR2(20), detail BLOB);

// 1. 插入空 BLOB 定位器
conn.setAutoCommit(false);
try (Statement stmt = conn.createStatement()) {
    stmt.executeUpdate("INSERT INTO tb_file VALUES ('image.gif', EMPTY_BLOB())");
}

// 2. 查回 BLOB 定位器 (FOR UPDATE 鎖定)
Blob blob;
try (ResultSet rs = stmt.executeQuery(
        "SELECT detail FROM tb_file WHERE name='image.gif' FOR UPDATE")) {
    rs.next();
    blob = rs.getBlob(1);
}

// 3. 取得輸出流寫入
try (OutputStream out = ((oracle.sql.BLOB)blob).getBinaryOutputStream();
     InputStream fin = new FileInputStream(file)) {
    byte[] buf = new byte[blob.getBufferSize()];
    int len;
    while ((len = fin.read(buf)) != -1) out.write(buf, 0, len);
}
conn.commit();

讀取:Blob blob = rs.getBlob("detail"); InputStream in = blob.getBinaryStream();

方案 C:JDBC 4.0+ 標準 API (推薦新專案)


// 寫入
try (PreparedStatement ps = conn.prepareStatement("INSERT INTO tb_file VALUES (?, ?)");
     InputStream fin = new FileInputStream(file)) {
    ps.setString(1, file.getName());
    ps.setBlob(2, fin);  // JDBC 4.0 標準,驅動內部處理 EMPTY_BLOB + 寫入
    ps.executeUpdate();
}

// 讀取
try (ResultSet rs = stmt.executeQuery("SELECT detail FROM tb_file WHERE name=?")) {
    rs.next();
    try (InputStream in = rs.getBlob(1).getBinaryStream()) {
        // 處理流
    }
}

總結:Oracle 11g+ 驅動完整支援 JDBC 4.0 setBlob/getBlob,請直接使用標準 API,避免 Oracle 專有類別綁定。

問題三:可捲動結果集 —— 記憶體殺手

  • Oracle **不支援伺服器端游標捲動** (TYPE_SCROLL_INSENSITIVE/SENSITIVE)。
  • JDBC 驅動在客戶端記憶體**全量緩存**結果集實現捲動。
  • 查詢 100 萬列 → OOM 風險極高;網路上 99% 教學未提此風險。

正確做法:

  1. 預設使用 TYPE_FORWARD_ONLY (預設),單向流式處理。
  2. 需分頁 → SQL 層面 OFFSET ... FETCH NEXT ... (12c+) 或 ROWNUM 分頁。
  3. 需隨機存取 → 先查主鍵陣列,再按主鍵逐筆查詢。

綜合建議

  1. 驅動載入:標準環境 Class.forName,特殊環境手動註冊。
  2. LOB:全面遷移至 JDBC 4.0 setBlob/getBlob,棄用 LONG RAW 及 Oracle 專有 oracle.sql.BLOB。
  3. 結果集:堅決不用可捲動,善用 SQL 分頁與流式處理。
  4. 連線池:生產環境必須使用成熟連線池 (HikariCP, DBCP2, Tomcat JDBC Pool),設定 maxLifetime, idleTimeout, connectionTestQuery。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved