【YashanDB知識庫】YashanDB的JDBC/OCI驅動如何設定字元編碼

YashanDB發表於2024-07-25

問題現象

Oracle、Mysql資料庫連結串,JDBC驅動連線串可以指定客戶端的編碼格式:

jdbc:mysql://hostname:port/database_name?useUnicode=true&characterEncoding=utf8mb4

jdbc:oracle:thin:@//hostname:port/service_name?NLS_LANGUAGE=AMERICAN&NLS_TERRITORY=AMERICA&NLS_CHARACTERSET=UTF8

YashanDB JDBC連線串沒有對應的引數:連線資料庫 | YashanDB Doc

經常收到客戶的反饋,YashanDB JDBC沒有對應的字元編碼引數設定,客戶端和服務端編碼不一致,要怎麼處理?同樣的OCI介面是否有對應的字元編碼引數可以設定?

問題的風險及影響

YashanDB已解決,無風險。

問題影響的版本

YashanDB JDBC/OCI驅動所有版本

問題發生原因

使用問題,詳見問題分析和處理過程。

解決方法及規避方式

非問題,無須規避

問題分析和處理過程

瞭解字元編碼

通常我們會遇到UTF-8、GBK,為了解背後的機制,還需要了解字符集、編碼的一些知識:

  • 字符集:抽象二進位制和字元間的對應關係,這套對應關係不考慮具體實現,只確定對映本身。GBK就是一套字符集。

  • 編碼:計算機二進位制和字元間的對應關係的實際編碼實現,這套對映體現在計算機實際儲存字串的二進位制序列上。UTF-8就是一種編碼的方式。

  • ASCII 碼:一共規定了128個字元的編碼,最前面的一位統一規定為0

  • Unicode:國際標準字符集,現在的規模可以容納100多萬個符號。每個符號的編碼都不一樣。

  • UTF-8:Unicode Transformation Format,網際網路上使用最廣的Unicode的一種實現,對英文使用8位(即一個位元組),中文使用24位(三個位元組)來編碼,另外還有UTF-16、Oracle常見的AL32UTF8等

  • GBK: 嚴格來說是漢字字符集定義,也可以看做字元編碼方式,因為它定義漢字字符集的同時也規定了如何將這些字元編碼轉換為二進位制位元組序列。有下面多種,GB2312使用2個位元組來編碼。

GBK、GB2312等與UTF8之間透過Unicode編碼能相互轉換:

  • GBK、GB2312 --先轉--> Unicode --再轉--> UTF8

  • UTF8 --先轉--> Unicode --再轉--> GBK、GB2312

相應的資料比較多,可以參考這篇:字元編碼那點事:快速理解ASCII、Unicode、GBK和UTF-8 - 知乎 (zhihu.com)

YashanDB JDBC自動轉碼

JAVA的StringCoding提供了字元轉換工具,YashanDB JDBC驅動利用了該工具實現了自動編解碼:

首先驅動會讀取JVM的編碼設定,假如服務端字符集與JVM字符集不同,則:

  • 把資料從客戶端傳到服務端時,JDBC自動按照服務端設定的字符集進行轉換。

  • 從服務端傳資料到客戶端時,JDBC按照客戶端設定的字符集進行編碼。

/**
 * Encodes this {@code String} into a sequence of bytes using the given
 * {@linkplain java.nio.charset.Charset charset}, storing the result into a
 * new byte array.
 *
 * <p> This method always replaces malformed-input and unmappable-character
 * sequences with this charset's default replacement byte array. The
 * {@link java.nio.charset.CharsetEncoder} class should be used when more
 * control over the encoding process is required.
 *
 * @param charset
 * The {@linkplain java.nio.charset.Charset} to be used to encode
 * the {@code String}
 *
 * @return The resultant byte array
 *
 * @since 1.6
 */
public byte[] getBytes(Charset charset) {
    if (charset == null) throw new NullPointerException();
    return StringCoding.encode(charset, value, 0, value.length);
}

所以無論在什麼情況下都不會出現亂碼問題,使用者不需要去關心JDBC字符集,也不需要設定字符集。

YashanDB OCI指定客戶端編碼

OCI需要指定客戶端的字符集,相關的語句:

errcode = OCIEnvNlsCreate((OCIEnv**)&envhpSessionRelease, (ub4)OCI_THREADED, (dvoid*)0,
(dvoid * (*)(dvoid*, size_t))0, (dvoid * (*)(dvoid*, dvoid*, size_t))0,
(void (*)(dvoid*, dvoid*))0, (size_t)0, (dvoid**)0, 852, 0);

目前崖山只支援852和871:

#define YCI_UTF8ID 871
#define YCI_ZHS16GBK 852

例如要指定編碼格式為GBK,就把852透過該介面傳進去,崖山的OCI介面就可以透過852來識別出是要支援 ZHS16GBK,具體支援的值對應的字符集參考:oracle Nls_Charset_Id 字符集編碼表_1507對應的字符集編碼-CSDN部落格

經驗總結

1、JDBC不需要指定編碼格式,會自動編解碼。

2、OCI需要透過介面OCIEnvNlsCreate指定編碼格式,目前只支援2種編碼。

相關文章