資料庫 CLOB(Character Large Object)用於存儲超過 VARCHAR 上限的大文本(XML、JSON、日誌、長文)。本文示範 JDBC 標準 API 寫入/讀取 CLOB 的正確姿勢,避免記憶體溢出與流未關閉問題。
一、寫入 CLOB(推薦:流式寫入)
String sql = "INSERT INTO doc(content) VALUES(?)";
try (Connection conn = ds.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
// 方式 1:JDBC 4.0+ 直接 setCharacterStream(推薦,無需建立 Clob 物件)
try (Reader reader = new StringReader(largeText)) {
ps.setCharacterStream(1, reader, largeText.length());
ps.executeUpdate();
}
// 方式 2:建立 Clob 物件(舊版驅動兼容)
// Clob clob = conn.createClob();
// clob.setString(1, largeText);
// ps.setClob(1, clob);
}
setCharacterStream 避免一次性載入巨大字串到 JVM 堆,直接流式傳輸到資料庫。
- 若文本來源為檔案,改用
new FileReader(file) 即可零拷貝上傳。
二、讀取 CLOB(推薦:流式讀取)
String sql = "SELECT content FROM doc WHERE id=?";
try (Connection conn = ds.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery()) {
ps.setLong(1, docId);
if (rs.next()) {
// 方式 1:getCharacterStream(推薦,流式處理)
try (Reader reader = rs.getCharacterStream("content");
StringWriter writer = new StringWriter()) {
char[] buffer = new char[8192];
int len;
while ((len = reader.read(buffer)) != -1) {
writer.write(buffer, 0, len);
}
String content = writer.toString();
// 處理 content...
}
// 方式 2:getAsciiStream(僅適用 ASCII/ISO-8859-1,中文會亂碼)
// InputStream is = rs.getAsciiStream("content");
// 方式 3:getSubString(一次性載入,慎用於大 CLOB)
// String content = rs.getClob("content").getSubString(1, (int) clob.length());
}
}
三、CLOB vs BLOB 選型
| 特性 | CLOB | BLOB |
| 存儲內容 | 字符文本(隱含字符集) | 二進制字節流 |
| JDBC 讀取 | getCharacterStream/getClob | getBinaryStream/getBlob |
| 適用場景 | XML、JSON、日誌、文章 | 圖片、PDF、加密數據、壓縮包 |
| 字符集處理 | 資料庫/驅動自動轉碼 | 應用層自行編解碼 |
四、主流資料庫 CLOB 類型映射
| 資料庫 | CLOB 類型 | 最大長度 | 備註 |
| MySQL | LONGTEXT / TEXT | 4GB / 64KB | 實際為 BLOB 族,字符集由表定義 |
| PostgreSQL | TEXT / BYTEA | 1GB | TEXT 無長度限制,性能優於 CLOB |
| Oracle | CLOB / NCLOB | 128TB | 原生支援,DBMS_LOB 套件操作 |
| SQL Server | VARCHAR(MAX) / NVARCHAR(MAX) | 2GB | 2005+ 版本支援 |
五、效能優化建議
- 流式讀寫:嚴禁
getSubString(1, length) 或 clob.getAsciiStream() 全量載入記憶體。
- 分塊處理:超大文本(>100MB)採用分塊寫入/讀取,配合資料庫
DBMS_LOB.WRITEAPPEND(Oracle)或分頁查詢。
- 連線池預處理:複用
PreparedStatement 減少解析開銷。
- 壓縮存儲:文本壓縮(GZIP)後存
BLOB,讀取時解壓,節省 70%+ 空間。
六、常見錯誤與排查
| 錯誤 | 原因 | 修正 |
java.sql.SQLException: Closed Connection | ResultSet/Clob 綁定的連線已關閉 | 在連線關閉前完成 Clob 讀取,或複製內容到獨立字串 |
| 中文亂碼 | 使用 getAsciiStream 讀取 UTF-8 文本 | 改用 getCharacterStream 或確保資料庫/連線字符集一致 |
| OOM | 大 CLOB 一次性 getSubString | 改用流式讀取,分塊處理 |
七、總結
- 寫入:
ps.setCharacterStream(1, reader, length) 流式零拷貝。
- 讀取:
rs.getCharacterStream("col") + 緩衝區循環讀取。
- 大文本優先考慮壓縮存
BLOB 或對象存儲(S3/MinIO),資料庫僅存元數據引用。
- 上一篇文章: dom4j selectSingleNode 報錯 NoClassDefFoundError: JaxenException 根因與解決
- 下一篇文章: Java 二進制文件讀寫實戰:DataOutput/DataInput 序列化協議設計
Copyright ©
程式碼之家 All Rights Reserved