问题现象

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知识库】YashanDB的JDBC/OCI驱动如何设置字符编码_字符编码

经常收到客户的反馈,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个字节来编码。

【YashanDB知识库】YashanDB的JDBC/OCI驱动如何设置字符编码_客户端_02

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);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.

所以无论在什么情况下都不会出现乱码问题,用户不需要去关心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);
  • 1.
  • 2.
  • 3.

目前崖山只支持852和871:

#define YCI_UTF8ID 871
#define YCI_ZHS16GBK 852
  • 1.
  • 2.

例如要指定编码格式为GBK,就把852通过该接口传进去,崖山的OCI接口就可以通过852来识别出是要支持 ZHS16GBK,具体支持的值对应的字符集参考: oracle Nls_Charset_Id 字符集编码表_1507对应的字符集编码-CSDN博客

经验总结

1、JDBC不需要指定编码格式,会自动编解码。

2、OCI需要通过接口OCIEnvNlsCreate指定编码格式,目前只支持2种编码。