今天遇到一个问题,从teradata数据库,用java读取汉字的时候,如果是生僻字,就读不出来,显示为乱码和问号。
刚开始以为是客户端字体的问题,但是调整了半天,也没有什么用。后来以为是客户端解码的问题,尝试将gbk转换成utf-8,也没有解决问题。后来只好将数据表的字段改为unicode编码,才解决了问题。但是,仍然觉得这样的解决方案实在不够完美,对于数据库、jdbc、客户端之间的数据传输机制并没有理清楚。
思考了一下,从根本原理上来梳理数据库、jdbc、java客户端三者关系,终于搞明白了。
在理解这个关系之前,要有一点关于汉字编码的基础知识。
汉字的数量很多,包括常用字和生僻字。其中常用字,以前使用gb2312字符集来表示;生僻的汉字在gb2312里是没有登记的,这些汉字被收录到gbk字符集了。有了这个基础知识,后面的分析就好理解了 。
首先,数据库负责如实的记录客户打算存储的数据。如果客户将字段A的字符集设置为ascii,然后往这个字段里存入了汉字,而且是生僻的汉字。假如你使用某种工具,能够正常显示数据表中的生僻汉字,这就说明,这些汉字采用了gbk编码。(我用teradata sql assistant的确可以查出数据库中的生僻汉字,而且显示正常)
其次,如果我们用jdbc driver去读数据库,发生了什么事情呢?在连接数据库的时候,jdbc url里要写清楚,本次连接数据库,打算按什么字符集去解释数据库中的内容。像以上情况,数据库按ascii存放中文,那么jdbcurl里就要按charset=ascii去写,否则肯定就不一致。
其三,jdbc driver帮忙把数据从数据库里读出之后,还会重新组装成java string给程序,方便程序处理。问题其实出在这里。因为这里的组装,我们是不清楚的,是jdbc自己处理的,是个黑箱。但是我们可以猜一下。如果生僻字在数据库存储正常,用专用客户端查询显示正常,用jdbcdriver连接后查询,显示为”?”。很大的可能就是生僻字在数据库端采用gbk编码,到了客户端,按gb2312编码进行映射,结果生僻字映射不到,就会显示为”?”
其四,观察了一下jdbcurl的构成,其中果然有一个client_charset的属性被设置了,设置值为euc_CN。经查询,这个就是gb2312的另外一个名字。如果把euc_CN改为gbk,问题就解决了。生僻汉字被正确映射了,使用jdbc也能正确查询生僻汉字了。