Oracle BLOB 類型適用於存儲圖片、文檔等二進制大對象,優於 LONG 類型。但 BLOB 寫入機制特殊:必須先插入 EMPTY_BLOB() 創建游標,再查詢鎖定行獲取游標,最後通過游標輸出流寫入數據。本文提供 JDBC 原生實現與 Hibernate 封裝實現的完整代碼對比,幫助開發者選擇適合方案。
INSERT INTO table (id, blob_col) VALUES (?, EMPTY_BLOB()),創建 BLOB 游標。SELECT blob_col FROM table WHERE id = ? FOR UPDATE,必須 FOR UPDATE 鎖定行,防止併發衝突。oracle.sql.BLOB 對象,調用 getBinaryOutputStream() 寫入字節,最後 UPDATE table SET blob_col = ? WHERE id = ? 提交。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(); }
}
}
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()。public class Cat {
private String id;
private String name;
private Blob image; // java.sql.Blob
// getter/setter
}
<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>
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) {} }
}
}
EMPTY_BLOB() 插入與 FOR UPDATE 查詢(通過 LockMode.UPGRADE)。| 維度 | JDBC 原生 | Hibernate |
|---|---|---|
| 控制力 | 完全控制 SQL、游標、緩衝區 | 依賴框架默認行為,調優受限 |
| 代碼量 | 多(約 80 行) | 少(約 30 行) |
| 可移植性 | 差(綁定 Oracle API) | 較好(標準 java.sql.Blob) |
| 適用場景 | 高性能批量導入、特殊優化需求 | 常規業務開發、團隊統一 ORM 棧 |
save() → flush() → refresh(UPGRADE) → 寫入流 → commit() 即可完成,推薦常規項目使用。