有的时候给Oracle的blob赋值会出现乱码,通常是程序编码的方式和数据用得编码方式不一样。像我之前就是程序用得是UTF-8,而Oracle数据库则是GBK。
总的来所分俩种情况:
- 程序和数据编码一致:
-- 注意rawtohex()函数是用数据库字符编码格式来生成十六进制字符串的 update act_hi_comment t set t.full_msg_ = rawtohex('哈哈哈哈哈') where t.id_ = '1234';
- 程序和数据编码不一致
-- 先用程序用自己的编码方式将字符串转换成十六进制字符串 update act_hi_comment t set t.full_msg_ = '这里是十六进制字符串' where id_ = '111';
create table ACT_HI_COMMENT
(
id_ NVARCHAR2(64) not null,
type_ NVARCHAR2(255),
time_ TIMESTAMP(6) not null,
user_id_ NVARCHAR2(255),
task_id_ NVARCHAR2(64),
proc_inst_id_ NVARCHAR2(64),
action_ NVARCHAR2(255),
message_ NVARCHAR2(2000),
full_msg_ BLOB
);
Oracle 给blob字段设置值的时候,它是可以接收十六进制的值,所以我们可以这样操作:
-- update act_hi_comment t set t.full_msg_ = '这里可以是十六进制字符串';
-- 或者下面这种形式
-- 注意rawtohex()函数是用数据库字符编码格式来生成十六进制字符串的
update act_hi_comment t set t.full_msg_ = rawtohex('哈哈哈哈哈') where t.id_ = '1234';
如果你的程序显示不是乱码了,那么就看到这里吧。
如果还是乱码,或者想了解更详细就继续往下
首先连接Oracle分thin和oci方式:(如:jdbc:oracle:thin:@10.1.1.2:1521:shdb jdbc:oracle:oci:@shdb JDBC驱动oci和thin区别)
-
走jdbc方式连接数据库,则客户端发送数据的编码都是UTF-8;
-
走oci方式连接数据库,则根据数据库的编码方式决定客户端的编码方式:
(A)数据库采样UTF-8的时候,客户端发送的编码也是UTF-8;
(B)数据库采用GBK的时候,则客户端发送的编码是GBK。
-- 查看oracle服务端编码:
select * from sys.nls_database_parameters where parameter = 'NLS_CHARACTERSET';
-- ZHS16GBK
-- 查看client编码:
select * from sys.nls_instance_parameters where parameter = 'NLS_LANGUAGE';
-- SIMPLIFIED CHINESE
由上面原因可知,java程序连oracle走JDBC,传的都是UTF-8编码格式的内容;而PLSQL和oralce是走oci去连接的,用的编码是GBK。
这也就是PLSQL里面进行的update,java读取出来后交页面显示是乱码的原因了(PLSQL用GBK编码存进数据库,java用UTF-8解码读出来,编码和解码不一致)。
所以只要保证存进去的时候用UTF-8编码即可。于是我就用java程序把字符按照UTF-8编码成二进制,然后再二进制转十六进制,最后把得到的十六进制的字符串update进去
public class Test {
public static void main(String[] args) {
String name = "我是一段字符串";
System.out.println(bytesToHexString(name.getBytes(StandardCharsets.UTF_8)));
// E68891E698AFE4B880E6AEB5E5AD97E7ACA6E4B8B2
// System.out.println(bytesToHexString(name.getBytes(Charset.forName("GBK"))));
// CED2CAC7D2BBB6CED7D6B7FBB4AE
}
public static String bytesToHexString(byte[] bArr) {
StringBuilder sb = new StringBuilder(bArr.length);
String sTmp;
for (byte b : bArr) {
sTmp = Integer.toHexString(0xFF & b);
if (sTmp.length() < 2) {
sb.append(0);
}
sb.append(sTmp.toUpperCase());
}
return sb.toString();
}
}
update act_hi_comment t set t.full_msg_ = 'E68891E698AFE4B880E6AEB5E5AD97E7ACA6E4B8B2' where t.id_ = '1234';
这样就可以搞定了。
(题外话:当中文字符以thin方式存储到数据库时,数据库还是会对照的转换成oracle服务端编码(在这里是GBK);查数据时还好再转回UTF-8。这也就是明明数据库是GBK编码,而Java程序中用UTF-8,却不会乱码的原因)
补充1种Oracle方式:
select convert(UTL_RAW.CAST_TO_VARCHAR2(t.full_msg_), 'zhs16gbk', 'AL32UTF8'), t.*
from wf.act_hi_comment t where t.proc_inst_id_ ='415696';
select UTL_RAW.CAST_TO_VARCHAR2(UTL_RAW.CONVERT(t.full_msg_, 'AMERICAN_AMERICA.zhs16gbk', 'AMERICAN_AMERICA.AL32UTF8'))
from wf.act_hi_comment t where t.proc_inst_id_ ='415696';