日期:2014-05-16  浏览次数:20623 次

两个数据库字符集不一样,如何快速增量同步数据.
环境:

DB-A 字符集:US7ASCII
DB-B 字符集:ZHS16GBK

需求: 从DB-A中将一个表的中文数据通过JAVA定时任务同步到DB-B.

DB-A库中表的信息如下:
CREATE TABLE USER_INFO (USER_ID number, USER_NAME varchar2(50));

如果用一般的方式来SELECT,和INSERT,不管客户端用什么样的字符集,数据到DB-B上肯定会显示乱码.


解决方法1 (在数据库实现)

解决思路:通过视图将数据转换成二进制数据,绕过字符集的转换,然后让目标库来读取.
1. 在DB-A库上创建视图:
create or replace view view_user_info
as
select user_id, utl_raw.cast_to_raw(user_name) user_name from user_info ;

2. 在DB-B库上创建视图:
create or replace view view_user_info_db_a
as
select user_id, utl_raw.cast_to_varchar2 (user_name) user_name from view_user_info@db_a;

3. 在DB-B库上进行数据读取:
select user_id,user_name from view_user_info_db_a ;
这时显示出来的数据才是正确的!

其实就是:select utl_raw.cast_to_raw('中华人民'),utl_raw.cast_to_varchar2('D6D0BBAAC8CBC3F1') from dual;
优点: 对JAVA程序来说是透明的,无所改动代码。
数据提取时,与客户端字符集无关
缺点: 增加数据库CPU开销

--经过钟MM的提示学习到了以下方法:

解决方法2 (在JAVA实现)

使用JAVA的两个函数来实现:getBytes() , new String()
先来看一下这两个函数:

. getBytes(charset)

这是java字符串处理的一个标准函数,其作用是将字符串所表示的字符按照charset编码,并以字节方式表示。
注意字符串在java内存中总是按unicode编码存储的。
比如"中文",正常情况下(即没有错误的时候)存储为"4e2d 6587",
如果charset为"gbk",则被编码为"d6d0 cec4",然后返回字节"d6 d0 ce c4"。
如果charset为"utf8"则最后是"e4 b8 ad e6 96 87"。
如果是"iso8859-1",则由于无法编码,最后返回 "3f 3f"(两个问号)。

. new String(charset)

这是java字符串处理的另一个标准函数,和上一个函数的作用相反,将字节数组按照charset编码进行组合识别,最后转换为unicode存储。
参考上述getBytes的例子,"gbk" 和"utf8"都可以得出正确的结果"4e2d 6587",但iso8859-1最后变成了"003f 003f"(两个问号)。
因为utf8可以用来表示/编码所有字符,所以new String( str.getBytes( "utf8" ), "utf8" ) === str,即完全可逆。

对于以上两个函数,我们可以有两种用法来解决这个问题:
方法 2.1

a) 无需关心客户端NLS_LANG的设置
b) 从数据库取出字符的二进制编码:
select user_id, utl_raw.cast_to_raw(user_name) user_name from user_info ;
c) 在java用其所 new String(username, 'GBK' ) 转成中文
d) 插入到目标库

方法 2.2 (推荐)

a) 将JAVA 客户端NLS_LANG设置为US7ASCII
b) 从数据库直接取出字段值
select user_id, user_name from user_info ;
(这时因为客户端和服务器的字符集一致,所以会得到正确的中文显示)
c) 将中文进行转码为GBK:
new String( user_name.getBytes( "gbk" ), "gbk" )
d) 得到正确的GBK中文编码插入到目标库

优点:代码完全由JAVA控制,无需作数据库变更,数据库的压力转移到了JAVA客户端.
注意:必须把客户端的字符集和服务器端的字符集设置成一样