大對象字段(CLOB/BLOB)在不同數據庫、不同持久層框架中類型映射與操作 API 差異較大。本文系統梳理類型對應關係,並給出 JDBC 原生與 Hibernate 兩套完整代碼範例。
| 場景 | CLOB 對應 | BLOB 對應 |
|---|---|---|
| MySQL 列類型 | TEXT / LONGTEXT | BLOB / LONGBLOB |
| Oracle/DB2 列類型 | CLOB | BLOB |
| Java 領域類型 | String / java.sql.Clob | byte[] / java.sql.Blob |
| Hibernate 映射類型 | clob / materialized_clob | blob / materialized_blob |
| 廠商擴展類型 | oracle.sql.CLOB | oracle.sql.BLOB |
conn.setAutoCommit(false);
// 階段 1:插入 EMPTY_CLOB()
PreparedStatement ps = conn.prepareStatement(
"insert into files(name, content) values(?, EMPTY_CLOB())");
ps.setString(1, "jack");
ps.executeUpdate();
// 階段 2:SELECT ... FOR UPDATE 鎖定行
ps = conn.prepareStatement("select content from files where name=? for update");
ps.setString(1, "jack");
ResultSet rs = ps.executeQuery();
rs.next();
oracle.sql.Clob clob = (oracle.sql.Clob) rs.getClob(1);
// 寫入字符流
try (Writer w = clob.getCharacterOutputStream()) {
w.write(longTextContent);
}
conn.commit();
oracle.sql.Clob clob = (oracle.sql.Clob) rs.getClob("content");
if (clob != null) {
try (Reader r = clob.getCharacterStream();
BufferedReader br = new BufferedReader(r)) {
String line, content = "";
while ((line = br.readLine()) != null) content += line + "
";
}
}
ps = conn.prepareStatement("insert into photos(name, data) values(?, empty_blob())");
ps.setString(1, "jack"); ps.executeUpdate();
ps = conn.prepareStatement("select data from photos where name=? for update");
ps.setString(1, "jack");
rs = ps.executeQuery(); rs.next();
oracle.sql.Blob blob = (oracle.sql.Blob) rs.getBlob(1);
try (OutputStream out = blob.getBinaryOutputStream();
FileInputStream fin = new FileInputStream("c:\\test.rar")) {
byte[] buf = new byte[blob.getBufferSize()];
int len;
while ((len = fin.read(buf)) != -1) out.write(buf, 0, len);
}
conn.commit();
Session session = ...;
Transaction tx = session.beginTransaction();
MyFile f = new MyFile(); f.setName("jack");
f.setContent(Hibernate.createClob("")); // 插入空 CLOB
session.save(f);
session.flush();
session.refresh(f, LockMode.UPGRADE); // 相當於 SELECT ... FOR UPDATE
oracle.sql.Clob clob = (oracle.sql.Clob) f.getContent();
try (Writer w = clob.getCharacterOutputStream()) {
w.write(longText);
}
tx.commit();
Photo p = new Photo(); p.setName("jack");
p.setPhoto(Hibernate.createBlob(new byte[0])); // 空 BLOB
session.save(p);
session.flush();
session.refresh(p, LockMode.UPGRADE);
oracle.sql.Blob blob = (oracle.sql.Blob) p.getPhoto();
try (OutputStream out = blob.getBinaryOutputStream();
FileInputStream fin = new FileInputStream("c:\\test.rar")) {
byte[] data = new byte[fin.available()];
fin.read(data);
out.write(data);
}
session.flush();
Hibernate.createClob/Blob + refresh(LockMode.UPGRADE) 封裝了兩階段邏輯,領域模型更純淨;但綁定廠商實現類(oracle.sql.Clob)影響可移植性。@Lob + @JdbcTypeCode(SqlTypes.CLOB),配合標準 java.sql.Clob 接口,減少強轉。MySQL TEXT/BLOB 類型無需「空值+鎖行」兩階段,可直接 ps.setString()/ps.setBytes() 或 Hibernate @Lob String/byte[] 透明持久化。但超大對象(>16MB)仍建議分流存儲(對象存儲/文件系統 + DB 存路徑)。