環(huán)境:
DB-A 字符集:US7ASCII
DB-B 字符集:ZHS16GBK
需求: 從DB-A中將一個(gè)表的中文數(shù)據(jù)通過JAVA定時(shí)任務(wù)同步到DB-B.
DB-A庫中表的信息如下:
CREATE TABLE USER_INFO (USER_ID number, USER_NAME varchar2(50));
如果用一般的方式來SELECT,和INSERT,不管客戶端用什么樣的字符集,數(shù)據(jù)到DB-B上肯定會(huì)顯示亂碼.
解決方法1 (在數(shù)據(jù)庫實(shí)現(xiàn))
解決思路:通過視圖將數(shù)據(jù)轉(zhuǎn)換成二進(jìn)制數(shù)據(jù),繞過字符集的轉(zhuǎn)換,然后讓目標(biāo)庫來讀取.
1. 在DB-A庫上創(chuàng)建視圖:
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庫上創(chuàng)建視圖:
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庫上進(jìn)行數(shù)據(jù)讀取:
select user_id,user_name from view_user_info_db_a ;
這時(shí)顯示出來的數(shù)據(jù)才是正確的!
其實(shí)就是:select utl_raw.cast_to_raw('中華人民'),utl_raw.cast_to_varchar2('D6D0BBAAC8CBC3F1') from dual;
優(yōu)點(diǎn): 對(duì)JAVA程序來說是透明的,無所改動(dòng)代碼。
數(shù)據(jù)提取時(shí),與客戶端字符集無關(guān)
缺點(diǎn): 增加數(shù)據(jù)庫CPU開銷
--經(jīng)過鐘MM的提示學(xué)習(xí)到了以下方法:
解決方法2 (在JAVA實(shí)現(xiàn))
使用JAVA的兩個(gè)函數(shù)來實(shí)現(xiàn):getBytes() , new String()
先來看一下這兩個(gè)函數(shù):
. getBytes(charset)
這是java字符串處理的一個(gè)標(biāo)準(zhǔn)函數(shù),其作用是將字符串所表示的字符按照charset編碼,并以字節(jié)方式表示。
注意字符串在java內(nèi)存中總是按unicode編碼存儲(chǔ)的。
比如"中文",正常情況下(即沒有錯(cuò)誤的時(shí)候)存儲(chǔ)為"4e2d 6587",
如果charset為"gbk",則被編碼為"d6d0 cec4",然后返回字節(jié)"d6 d0 ce c4"。
如果charset為"utf8"則最后是"e4 b8 ad e6 96 87"。
如果是"iso8859-1",則由于無法編碼,最后返回 "3f 3f"(兩個(gè)問號(hào))。
. new String(charset)
這是java字符串處理的另一個(gè)標(biāo)準(zhǔn)函數(shù),和上一個(gè)函數(shù)的作用相反,將字節(jié)數(shù)組按照charset編碼進(jìn)行組合識(shí)別,最后轉(zhuǎn)換為unicode存儲(chǔ)。
參考上述getBytes的例子,"gbk" 和"utf8"都可以得出正確的結(jié)果"4e2d 6587",但iso8859-1最后變成了"003f 003f"(兩個(gè)問號(hào))。
因?yàn)閡tf8可以用來表示/編碼所有字符,所以new String( str.getBytes( "utf8" ), "utf8" ) === str,即完全可逆。
對(duì)于以上兩個(gè)函數(shù),我們可以有兩種用法來解決這個(gè)問題:
方法 2.1
a) 無需關(guān)心客戶端NLS_LANG的設(shè)置
b) 從數(shù)據(jù)庫取出字符的二進(jìn)制編碼:
select user_id, utl_raw.cast_to_raw(user_name) user_name from user_info ;
c) 在java用其所 new String(username, 'GBK' ) 轉(zhuǎn)成中文
d) 插入到目標(biāo)庫
方法 2.2 (推薦)
a) 將JAVA 客戶端NLS_LANG設(shè)置為US7ASCII
b) 從數(shù)據(jù)庫直接取出字段值
select user_id, user_name from user_info ;
(這時(shí)因?yàn)榭蛻舳撕头?wù)器的字符集一致,所以會(huì)得到正確的中文顯示)
c) 將中文進(jìn)行轉(zhuǎn)碼為GBK:
new String( user_name.getBytes( "gbk" ), "gbk" )
d) 得到正確的GBK中文編碼插入到目標(biāo)庫
優(yōu)點(diǎn):代碼完全由JAVA控制,無需作數(shù)據(jù)庫變更,數(shù)據(jù)庫的壓力轉(zhuǎn)移到了JAVA客戶端.
注意:必須把客戶端的字符集和服務(wù)器端的字符集設(shè)置成一樣
聯(lián)系客服