我用这样的表设置我的数据库:
CREATE TABLE t_audit_log
(
description VARCHAR2 (2500)
);
在使用它的Java应用程序中,我使用Hibernate将数据类映射到它,并确保我不会生成SQLExceptions,我将此截断算法放在属性getter中:
private static final int MAX_STRING_LEN_2500 = 2499;
public void setDescription(final String newDescription) {
if (newDescription != null
&& newDescription.length() > MAX_STRING_LEN_2500) {
description = newDescription.substring(0, MAX_STRING_LEN_2500);
} else {
description = newDescription;
}
}
对于成千上万的审计日志条目,这种方法运行良好 - 直到今天。我在日志中找到了这个:
Nov 09, 2015 7:54:40 AM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
WARN: SQL Error: 12899, SQLState: 72000
Nov 09, 2015 7:54:40 AM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
ERROR: ORA-12899: value too large for column "BLABLA"."T_AUDIT_LOG"."DESCRIPTION"
(actual: 2501, maximum: 2500)
为什么substring()
在值中留下了额外的字符?
答案 0 :(得分:9)
我怀疑您的数据库设置被设置为使用"byte semantics"进行长度操作(这是NLS_LENGTH_SEMANTICS
的默认设置),在这种情况下,您说的是您希望字段在编码时长度最多为2500字节,而不是2500个字符。假设您的数据库使用UTF-8对字符串进行编码 - 如果您的字符串包含2498个ASCII字符和1个字符U + 20A0(欧元符号),则会产生总共2501个字节,但只有2499个字符。
Java length()
和substring()
操作将按照UTF-16代码单元运行 - 这些代码单元可能非常与"字符语义对齐&#34 34 ;. (你不太可能尝试在基本多语种平面之外存储字符,这是单个字符需要两个UTF-16代码单元的地方,但它是可能的。)
你真的需要找出你希望字段长度实际表示的内容 - 然后你可以弄清楚是否要改变你在Java中执行截断的方式。
答案 1 :(得分:7)
修改您的Oracle NLS_LENGTH_SEMANTICS,如果您使用的是BYTE或CHAR,则不指定,默认为BYTE。某些字符可能需要一个字节才能存储到数据库中,因此请尝试将表修改为
CREATE TABLE t_audit_log
(
description VARCHAR2 (2500 char)
);
再试一次。
<小时/>
来自Oracle docs:
NLS_LENGTH_SEMANTICS的会话级别值指定默认值 用于VARCHAR2和CHAR表列的长度语义, 用户定义的对象属性和数据库中的PL / SQL变量 在会话中创建的对象。 此默认值可能会被覆盖 显式长度语义限定符BYTE和CHAR in列, 属性和变量定义。