Oracle BLOB 特性
- 優於 LONG/LONG RAW:隨機存取、流式操作、效能佳。
- 需透過 Locator (游標) 讀寫,不能直接 INSERT 值。
JDBC 標準三步曲
- INSERT EMPTY_BLOB() 建立 Locator。
- SELECT ... FOR UPDATE 鎖定該列並取得 Locator。
- 透過 Locator 的 OutputStream 寫入資料,再 UPDATE 回寫(或同一交易內自動同步)。
完整程式碼 (Oracle 專用 API)
Connection conn = DriverManager.getConnection(url, user, pass);
conn.setAutoCommit(false);
PreparedStatement pstmt = conn.prepareStatement(
"INSERT INTO javatest(name, content) VALUES (?, EMPTY_BLOB())");
pstmt.setString(1, "fankai");
pstmt.executeUpdate();
pstmt.close();
// 必須 FOR UPDATE 鎖列,取得可寫 Locator
pstmt = conn.prepareStatement(
"SELECT content FROM javatest WHERE name = ? FOR UPDATE");
pstmt.setString(1, "fankai");
ResultSet rset = pstmt.executeQuery();
BLOB blob = null;
if (rset.next()) blob = (BLOB) rset.getBlob(1);
rset.close();
pstmt.close();
// 寫入二進位資料
File file = new File("ojdbc.jar");
FileInputStream fin = new FileInputStream(file);
OutputStream out = blob.getBinaryOutputStream();
byte[] buffer = new byte[fin.available()];
fin.read(buffer);
out.write(buffer);
fin.close();
out.close();
// 明確 UPDATE(同一交易可省略,但建議明確)
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();
關鍵細節
java.sql.Blob (JDBC 標準) vs oracle.sql.BLOB (Oracle 擴充,含 getBinaryOutputStream()) —— 大小寫不同,別搞混。
- JDK 1.3 (JDBC 2.0) 僅支援讀 Blob,寫入需 JDK 1.4+ (JDBC 3.0) 或 Oracle 專用 API。
FOR UPDATE 必加,否則 Locator 唯讀,寫入拋異常。
- 大檔案分塊寫入:
byte[] buf = new byte[blob.getBufferSize()],迴圈 fin.read(buf) → out.write(buf, 0, len),避免 OOM。
Hibernate 簡化實作 (3.x/4.x/5.x 通用)
實體定義
@Entity
public class Cat {
@Id String id;
String name;
@Lob @Basic(fetch=FetchType.LAZY) Blob image; // 延遲載入
// getter/setter
}
寫入程式碼
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
Cat c = new Cat();
c.setName("Robbin");
// 先存入空 Blob 建立 Locator
c.setImage(Hibernate.createBlob(new byte[0]));
s.save(c);
s.flush(); // 強制同步取得生成的 Locator
// 重新載入並升級鎖,取得可寫 Blob
s.refresh(c, LockMode.UPGRADE);
Blob blob = c.getImage();
OutputStream out = ((oracle.sql.BLOB) blob).getBinaryOutputStream();
// ... 寫入檔案內容同 JDBC ...
out.close();
s.flush();
tx.commit();
s.close();
Hibernate 優勢
- 不寫 SQL,跨資料庫可攜性(理論上)。
- Session 管理交易、快取、脏檢查。
- 程式碼量減少 60%+,核心邏輯更清晰。
總結對比