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

Oracle BLOB 寫入完全指南:JDBC 與 Hibernate 兩種實現對比

     編輯:Java 相關知識

Oracle BLOB 類型適用於存儲圖片、文檔等二進制大對象,優於 LONG 類型。但 BLOB 寫入機制特殊:必須先插入 EMPTY_BLOB() 創建游標,再查詢鎖定行獲取游標,最後通過游標輸出流寫入數據。本文提供 JDBC 原生實現與 Hibernate 封裝實現的完整代碼對比,幫助開發者選擇適合方案。

一、Oracle BLOB 寫入三步驟原理

  1. 插入空 BLOB:INSERT INTO table (id, blob_col) VALUES (?, EMPTY_BLOB()),創建 BLOB 游標。
  2. 查詢鎖定獲取游標:SELECT blob_col FROM table WHERE id = ? FOR UPDATE,必須 FOR UPDATE 鎖定行,防止併發衝突。
  3. 通過游標寫入數據:獲取 oracle.sql.BLOB 對象,調用 getBinaryOutputStream() 寫入字節,最後 UPDATE table SET blob_col = ? WHERE id = ? 提交。

二、JDBC 原生實現完整代碼

import java.sql.*;
import java.io.*;
import oracle.sql.*;

public class WriteBlobJDBC {
    public static void main(String[] args) {
        try {
            DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
            Connection conn = DriverManager.getConnection(
                "jdbc:oracle:thin:@localhost:1521:orcl", "fankai", "fankai");
            conn.setAutoCommit(false);

            BLOB blob = null;
            PreparedStatement pstmt = conn.prepareStatement(
                "INSERT INTO javatest(name, content) VALUES (?, EMPTY_BLOB())");
            pstmt.setString(1, "fankai");
            pstmt.executeUpdate();
            pstmt.close();

            pstmt = conn.prepareStatement(
                "SELECT content FROM javatest WHERE name = ? FOR UPDATE");
            pstmt.setString(1, "fankai");
            ResultSet rset = pstmt.executeQuery();
            if (rset.next()) blob = (BLOB) rset.getBlob(1);
            pstmt.close();

            File file = new File("oraclejdbc.jar");
            FileInputStream fin = new FileInputStream(file);
            byte[] data = new byte[(int) file.length()];
            fin.read(data);
            fin.close();

            OutputStream out = blob.getBinaryOutputStream();
            out.write(data);
            out.close();

            pstmt = conn.prepareStatement(
                "UPDATE javatest SET content = ? WHERE name = ?");
            pstmt.setBlob(1, blob);
            pstmt.setString(2, "fankai");
            pstmt.executeUpdate();
            pstmt.close();

            conn.commit();
            conn.close();
        } catch (Exception e) { e.printStackTrace(); }
    }
}

關鍵注意點

  • FOR UPDATE:必須加鎖,否則併發環境下數據不一致。
  • oracle.sql.BLOB vs java.sql.Blob:大小寫不同,前者為 Oracle 擴展類,提供 getBinaryOutputStream();JDK 1.3 JDBC 2.0 僅支持讀 Blob,JDK 1.4 JDBC 3.0 才加入寫接口,建議直接用 Oracle API 兼容性更好。
  • 大文件分塊寫入:使用 blob.getBufferSize() 作為緩衝區大小循環讀寫,避免 OOM。
  • 事務控制:setAutoCommit(false),寫入完成後 commit()。

三、Hibernate 實現:簡化流程

1. 實體類映射

public class Cat {
    private String id;
    private String name;
    private Blob image; // java.sql.Blob
    // getter/setter
}

2. 映射文件

<class name="com.fankai.Cat" table="cat">
    <id name="id"><generator class="uuid.hex"/></id>
    <property name="name" length="16" not-null="true"/>
    <property name="image"/> <!-- 自動映射為 BLOB -->
</class>

3. 寫入代碼

public class TestCatHibernate {
    public static void testBlob() {
        Session s = null;
        try {
            SessionFactory sf = HibernateSessionFactory.getSessionFactory();
            s = sf.openSession();
            Transaction tx = s.beginTransaction();

            Cat c = new Cat();
            c.setName("Robbin");
            c.setImage(Hibernate.createBlob(new byte[]{1})); // 插入空 BLOB 佔位
            s.save(c);
            s.flush(); // 執行 INSERT EMPTY_BLOB()

            s.refresh(c, LockMode.UPGRADE); // 相當於 SELECT ... FOR UPDATE 獲取游標
            BLOB blob = (BLOB) c.getImage(); // 獲取 oracle.sql.BLOB

            OutputStream out = blob.getBinaryOutputStream();
            FileInputStream fin = new FileInputStream("oraclejdbc.jar");
            byte[] data = new byte[(int) new File("oraclejdbc.jar").length()];
            fin.read(data);
            out.write(data);
            fin.close();
            out.close();

            s.flush();
            tx.commit();
        } catch (Exception e) { e.printStackTrace(); }
        finally { if (s != null) try { s.close(); } catch (Exception e) {} }
    }
}

Hibernate 核心優勢

  • 自動處理 EMPTY_BLOB() 插入與 FOR UPDATE 查詢(通過 LockMode.UPGRADE)。
  • 無需手寫 Oracle 特有 SQL,可移植性較好(底層仍依賴 Oracle 方言實現)。
  • 代碼量減少約 60%,業務邏輯更清晰。

四、方案選型建議

維度JDBC 原生Hibernate
控制力完全控制 SQL、游標、緩衝區依賴框架默認行為,調優受限
代碼量多(約 80 行)少(約 30 行)
可移植性差(綁定 Oracle API)較好(標準 java.sql.Blob)
適用場景高性能批量導入、特殊優化需求常規業務開發、團隊統一 ORM 棧

五、總結

  • Oracle BLOB 寫入核心是 三步驟:空插入 → 鎖定查詢 → 游標寫入。
  • JDBC 實現需手動管理每步,適合極致性能場景。
  • Hibernate 封裝了標準流程,save() → flush() → refresh(UPGRADE) → 寫入流 → commit() 即可完成,推薦常規項目使用。
  • 無論哪種方案,FOR UPDATE 鎖行、流關閉、事務提交、大文件分塊 是不可省略的工程化細節。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved