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

Hibernate 映射 CLOB 與 BLOB 大型物件完整指南

     編輯:Java 相關知識

大型物件型別對照表

Hibernate 型別Java 型別資料庫對應適用場景
blob / java.sql.Blobjava.sql.Blob, byte[]Oracle BLOB, MySQL BLOB, SQL Server IMAGE圖片、音訊、影片、二進位檔案
clob / java.sql.Clobjava.sql.Clob, StringOracle CLOB, MySQL TEXT/LONGTEXT, SQL Server NTEXT大型文本、JSON、XML、日誌內容
materialized_blobbyte[]同上小型二進位資料,直接實體化在記憶體
materialized_clobString同上小型文本,直接實體化在記憶體

映射檔配置

HBM XML 映射

<hibernate-mapping>
    <class name="com.example.User" table="users">
        <id name="id" column="user_id">
            <generator class="native"/>
        </id>
        
        <!-- BLOB 映射 -->
        <property name="image" column="user_image" type="blob"/>
        <!-- 或明確指定 -->
        <property name="image" column="user_image" type="java.sql.Blob"/>
        
        <!-- CLOB 映射 -->
        <property name="description" column="user_desc" type="clob"/>
        <!-- 或 -->
        <property name="description" column="user_desc" type="java.sql.Clob"/>
    </class>
</hibernate-mapping>

JPA 註解映射 (Hibernate 相容)

@Entity
@Table(name = "users")
public class User {
    @Id @GeneratedValue
    private Long id;

    @Lob
    @Column(name = "user_image", columnDefinition = "BLOB")
    private Blob image;  // 或 byte[]

    @Lob
    @Column(name = "user_desc", columnDefinition = "CLOB")
    private Clob description;  // 或 String

    // Getters/Setters...
}

Entity 類別定義 (POJO)

public class User {
    private Long id;
    private Blob image;        // BLOB 欄位
    private Clob description;  // CLOB 欄位
    
    // 標準 Getter/Setter
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public Blob getImage() { return image; }
    public void setImage(Blob image) { this.image = image; }
    public Clob getDescription() { return description; }
    public void setDescription(Clob description) { this.description = description; }
}

儲存 BLOB 與 CLOB 範例

標準 Hibernate API (跨資料庫)

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

User user = new User();

// 1. 存入圖片 (BLOB)
FileInputStream fis = new FileInputStream("image.jpg");
Blob imageBlob = Hibernate.getLobCreator(session).createBlob(fis, fis.available());
user.setImage(imageBlob);
fis.close();

// 2. 存入大型文本 (CLOB)
Clob descClob = Hibernate.getLobCreator(session).createClob("這是一段很長的文字描述...");
user.setDescription(descClob);

session.save(user);
tx.commit();
session.close();

Hibernate 3/4 簡化寫法

// 直接使用工廠方法 (舊版 Hibernate)
Blob img = Hibernate.createBlob(inputStream);
Clob desc = Hibernate.createClob("文字內容");
user.setImage(img);
user.setDescription(desc);

讀取 BLOB 與 CLOB 範例

讀取 BLOB 並寫入檔案

User user = (User) session.get(User.class, userId);
Blob blob = user.getImage();

if (blob != null) {
    InputStream in = blob.getBinaryStream();
    FileOutputStream fos = new FileOutputStream("output.jpg");
    
    byte[] buffer = new byte[4096];
    int len;
    while ((len = in.read(buffer)) != -1) {
        fos.write(buffer, 0, len);
    }
    fos.close();
    in.close();
}

讀取 CLOB 文字內容

Clob clob = user.getDescription();
if (clob != null) {
    Reader reader = clob.getCharacterStream();
    BufferedReader br = new BufferedReader(reader);
    StringBuilder sb = new StringBuilder();
    String line;
    while ((line = br.readLine()) != null) {
        sb.append(line).append("\n");
    }
    br.close();
    System.out.println(sb.toString());
}

Oracle 特有問題與解決方案

問題:streams type cannot be used in batching

原因: Oracle JDBC 驅動不支援批次處理中的串流操作。Oracle BLOB/CLOB 採用串流機制讀寫,與批次更新衝突。

解決方案:兩階段儲存法

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

User user = new User();

// 階段 1:先存入空的 LOB 佔位符
user.setImage(Hibernate.createBlob(new byte[1]));      // 1-byte 空 BLOB
user.setDescription(Hibernate.createClob(" "));        // 空白 CLOB
session.save(user);
session.flush();                                       // 強制同步到資料庫

// 階段 2:重新載入並鎖定該列 (FOR UPDATE)
session.refresh(user, LockMode.UPGRADE);

// 階段 3:寫入實際內容
Blob blob = user.getImage();
OutputStream out = blob.setBinaryStream(1);  // 位置 1 開始寫入
FileInputStream fis = new FileInputStream("real_image.jpg");
byte[] buffer = new byte[4096];
int len;
while ((len = fis.read(buffer)) != -1) {
    out.write(buffer, 0, len);
}
fis.close();
out.close();

// CLOB 寫入 (Oracle 專用 API)
oracle.sql.Clob oracleClob = (oracle.sql.Clob) user.getDescription();
Writer writer = oracleClob.getCharacterOutputStream();
writer.write("真正的長文本內容...");
writer.close();

tx.commit();
session.close();

效能優化建議

  1. 分塊讀寫: 避免一次性載入大型 LOB 到記憶體,使用 4KB-8KB 緩衝區循環
  2. 啟用 LOB 緩存: Oracle 可設定 lobPrefetchSize 減少往返
  3. 避免不必要的 LOB 載入: 查詢列表頁面時,不要 SELECT LOB 欄位,改用投影查詢
  4. 資料庫層面壓縮: Oracle SecureFiles、MySQL COMPRESSED 行格式
  5. 考慮外部儲存: 超大檔案 (GB 級) 建議存檔案系統/物件儲存 (S3),資料庫僅存路徑

常見錯誤與除錯

| 錯誤現象 | 原因 | 解決方案 | |-----------|------|----------| | `NullPointerException` 存取 LOB | Session 已關閉,LOB 串流需在開啟的 Session 中讀取 | 確保在 Session/Transaction 範圍內讀取,或啟用 `hibernate.enable_lazy_load_no_trans` | | 寫入後讀取為空 | 未 `flush()` 或未 `commit()` | 確認交易正確提交 | | Oracle `ORA-22990` | LOB 定位器失效 | 使用兩階段儲存法,`refresh(LockMode.UPGRADE)` 重新取得有效定位器 | | MySQL `Packet too large` | 超過 `max_allowed_packet` | 增加資料庫參數 `max_allowed_packet=64M` 或分塊寫入 |

總結

Hibernate 對 BLOB/CLOB 提供完善支援,關鍵在於:

  • 映射正確選型 (blob/clob vs materialized_*)
  • 使用 Hibernate.getLobCreator() 建立可攜性 LOB 物件
  • Oracle 特殊處理:兩階段儲存 + LockMode.UPGRADE
  • 讀寫均採串流分塊處理,避免 OOM
  • 大檔案考慮外部儲存架構
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved