| Hibernate 型別 | Java 型別 | 資料庫對應 | 適用場景 |
|---|---|---|---|
blob / java.sql.Blob | java.sql.Blob, byte[] | Oracle BLOB, MySQL BLOB, SQL Server IMAGE | 圖片、音訊、影片、二進位檔案 |
clob / java.sql.Clob | java.sql.Clob, String | Oracle CLOB, MySQL TEXT/LONGTEXT, SQL Server NTEXT | 大型文本、JSON、XML、日誌內容 |
materialized_blob | byte[] | 同上 | 小型二進位資料,直接實體化在記憶體 |
materialized_clob | String | 同上 | 小型文本,直接實體化在記憶體 |
<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>
@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...
}
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; }
}
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)
Blob img = Hibernate.createBlob(inputStream);
Clob desc = Hibernate.createClob("文字內容");
user.setImage(img);
user.setDescription(desc);
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 = 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 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();
lobPrefetchSize 減少往返Hibernate 對 BLOB/CLOB 提供完善支援,關鍵在於:
blob/clob vs materialized_*)Hibernate.getLobCreator() 建立可攜性 LOB 物件LockMode.UPGRADE