Hibernate 寫入 Oracle 時拋出 ORA-12899: value too large for column,明明已用 substring(0,512) 截斷,卻提示實際長度 521 超過上限 512。根因在於「字元數」與「位元組數」的單位差異。
ORA-12899: 列 "ITS"."VIO_BUSINESS_RESPONSE_I"."RESPONSE" 的值太大
(實際值: 521, 最大值: 512)
欄位定義:VARCHAR2(512)(預設 byte 語意)。
程式碼:str.substring(0, 512) 按字元截取。
VARCHAR2(n) 預設以位元組為單位。substring 按字元計數,512 個中文字元 = 1,536 位元組,遠超 512 位元組上限。// 粗略估算:中文佔 3 bytes,預留餘量
str.substring(0, 400) // 400 * 3 = 1200 bytes,仍可能超過,視實際比例調整
缺點:無法精確控制,純英文時浪費空間。
// Java 端處理
String encoded = URLEncoder.encode(str, "UTF-8"); // 中文變 %E4%B8%AD 等 9 字元
// 或直接取位元組陣列
byte[] bytes = str.getBytes("UTF-8");
if (bytes.length > 512) {
bytes = Arrays.copyOf(bytes, 512);
}
String safeStr = new String(bytes, "UTF-8"); // 注意:截斷可能產生半個中文導致解碼錯誤,建議用 CharsetDecoder
更穩健的做法:使用 CharsetDecoder 或 ByteBuffer 精確截取完整字元。
ALTER TABLE VIO_BUSINESS_RESPONSE_I
MODIFY RESPONSE VARCHAR2(512 CHAR);
優點:應用端按字元截取即可,無需擔心位元組轉換。
缺點:需評估現有資料遷移影響,且最大字元數仍受 4000 位元組硬性限制(MAX_STRING_SIZE=STANDARD 時)。
VARCHAR2(n CHAR),消除單位混淆。safeSubstring(str, maxBytes, charset) 工具方法。CHECK (LENGTHB(col) <= 512) 防呆。| 場景 | 欄位定義 | Java 截取方式 | 風險 |
|---|---|---|---|
| 舊系統未改 CHAR | VARCHAR2(512) | 按位元組截取 | 低 |
| 新系統改 CHAR | VARCHAR2(512 CHAR) | 按字元截取 | 無 |
| 混合環境 | VARCHAR2(512) | 先編碼再截位元組 | 中 |