`

java.sql.SQLException: ORA-01461: can bfor insert into a LONG column问题分析与小结

阅读更多

java.sql.SQLException: ORA-01461: can bfor insert into a LONG column

 

1.发现系统报此问题的第一反应是插入数据时,数据长度超出数据库的字段长度。

2.而根据错误日志不难分析出是向邮件发送信息表中插入数据时,数据超过定义的最大值限制了,但到底是哪个字段,需进一步看一下代码和日志。

3.根据错误日志的时间找到跟踪日志,找到组装邮件DTO的数据,一个一个排查,最终发现只可能是邮件内容超出长度了。

但邮件内容是有长度判断与处理的(即:判断是否超过数据库字段所设置的4000长度,如果超过了,进行截取处理)

String emailContent = null;
if(mailContent != null){
	emailContent= mailContent.buildMailContent(args);
}
if(emailContent!=null&&emailContent.length()>4000){
	emailContent=emailContent.substring(0, 4000);
}
mailDTO.setEmailContent(emailContent);

         进一步分析,发现邮件内容的组装有html标签及样式的定义;而查看跟踪日志,发现’<’被替换为’&lt;’、’>’被替换为’&gt;’,于是我就主观的认为是在保存至数据库时,'<'、'>'标签被转义处理(即’<’变成了’&lt;’,仅此一个’<’的长度由1变成了4)。

        后来,将跟踪日志中的邮件内容拷出,将其全部’&lt;’替换为’<’、’&gt;’替换为’>’,并将内容中的单引号再加一个单引号的转义处理,通过PL/SQL更新开发环境里的一条记录,报超出长度错误提示。经一步步减少字符长度,最终发现:“将内容去掉6个汉字或11个英文字母,都能正常更新成功”。按上面的在保存至数据库时,'<'、'>'标签被转义处理的原因推测,减少6个汉字或11个英文字母,应该还是会超出长度。

        于是,我将邮件内容中的所有’&lt;’替换为’<’、’&gt;’替换为’>’ ,在UltraEdit-32中发现其长度是4011(正好符合上面更新SQL  减少11个英文字母能正常更新),此时,想到原来代码扫描时的getByte()方法,有字符编码格式的问题。

     进一步,我将’&lt;’替换为’<’、’&gt;’替换为’>’的邮件内容字符串进行了如下测试:

//全部’&lt;’替换为’<’,全部’&gt;’替换为’>’
String res3 = "<html><style>th , td{font-size: 13px;color: #4f5b75;font-weight: bold;border: 1px solid #95a7bf;border-collapse: collapse;padding: 4px;height: 22px;}</style><body>尊敬的XXX:<br />  您好!<br />  ... 此处内容省略 ... </body></html>";
System.out.println("res3:" + res3.length());  //结果是:3801
		
byte[] b_str = res3.getBytes();
System.out.println("b_str:" + b_str.length);  //结果是:4011

        分析到这里,我们应该知道原因了。

    原因是:

    str.length()返回此字符串的长度。长度等于字符串中 Unicode 代码单元的数量。即不论是中文还是英文,都是按照1个长度来看待的,而不是根据所占的字节数来计算length长度。

    而我们的ORACLE数据库存储格式应该是UTF-8或GBK之类(肯定不是JAVA中的Unicode 代码单元的数量)。

//全部’&lt;’替换为’<’,全部’&gt;’替换为’>’
String res3 = "<html><style>th , td{font-size: 13px;color: #4f5b75;font-weight: bold;border: 1px solid #95a7bf;border-collapse: collapse;padding: 4px;height: 22px;}</style><body>尊敬的XXX:<br />  您好!<br />  ... 此处内容省略 ... </body></html>";
System.out.println("res3:" + res3.length());
		
byte[] b_str = res3.getBytes();
System.out.println("b_str:" + b_str.length);

try {
	byte[] b_str3 = res3.getBytes("GBK");
	System.out.println("GBK b_str3:" + b_str3.length);  //结果:4011
			
	byte[] b_str2 = res3.getBytes("UTF-8");
	System.out.println("UTF-8 b_str2:" + b_str2.length);  //结果:4221
} catch (UnsupportedEncodingException e1) {
	// TODO Auto-generated catch block
	e1.printStackTrace();
}

        发现用GBK编码解析这串字符串长度正好是4011。据此反推:我们的数据库的编码格式是GBK。

    简单来说,系统在组装邮件对象后对邮件内容长度进行判断(长度是3801)是满足小于4000的条件的,而到后台保存至数据库时,由于数据库是GBK编码的,按GBK编码,其长度为4011,超出数据库字段的定义长度4000。所以报“ORA-01461: can bfor insert into a LONG column”错误。

    解决方法:

    将原来的如下校验

if(mailContent != null){
	emailContent= mailContent.buildMailContent(args);
}

     改为:

if(emailContent != null) {
	byte[] bytes = emailContent.getBytes("GBK");
	if(bytes.length > 4000) {
		int tempLen = new String(bytes, 0, 4000, "GBK").length();
		//根据tempLen长度截取原字符串
		emailContent = emailContent.substring(0, tempLen);
	}
}

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics