多国语言环境下联邦数据库代码页转换配置和常见问题解答
2010-06-18 00:00:00 来源:WEB开发网前言
在信息整合的架构中,IBM 联邦数据库与若干异构数据源连接并提供整体一致的服务。从数据源到联邦数据库再到具体应用程序,数据的流动和转换依赖于各个环节代码页的正确配置。由于数据源环境本身的异构性,联邦数据库在包装器中采用了不同的策略和实现方式,客户需要根据实际环境进行配置以实现联邦数据库和数据源之间的代码页转换。另外,区别于传统欧美用户的使用习惯,东亚用户更多采用 Unicode 或者其他多字节字符集,也增大了代码页配置过程中的复杂性。
本文首先总体介绍一下联邦数据库在多国语言环境下配置代码页转换几种情况;其次对于不同数据源的包装器(如 DRDA、Oralce、ODBC 等),分别介绍如何根据用户的需要进行配置;最后,对使用联邦数据库的代码转换常见问题进行了解答。
联邦数据库中代码页简介
联邦数据库中涉及到的代码页包括操作系统级别区域设置选项默认的代码页,DB2 客户端应用程序的代码页,DB2 数据库代码页,远程数据源的客户端代码页和远程数据库的代码页。
操作系统 Locale:系统级别的代码页设置,决定应用程序或数据库的默认代码页。
DB2CODEPAGE:实例级别的代码页设置,影响到所有的 DB2 客户端应用程序的代码页。
数据库的代码页:数据库级别的代码页设置,在 DB2 V9.5 及之后的版本,数据库代码页默认是 1208(UTF-8),但在创建数据库时可以通过“USING CODESET”子句显性指定代码页名称。数据库一旦创建成功,此代码页将不能改变。例如,您可以使用下面的 SQL 创建一个支持 GBK 中文编码的数据库。
CREATE DATABASE <database_name>USING CODESET GBK TERRITORY CN
作为分布式异构数据库集合的核心,联邦数据库提供更灵活的代码页转换功能。图 1 显示了联邦数据库代码页转换的总体框架。
图 1. 联邦数据库代码页转换的总体框架
查看原图(大图)
从图 1 中,您可以容易的看到从远程数据库到用户应用程序中的数据流。每双箭头表示可能发生的代码页转换过程,这些过程可能是受不同的参数影响。通常情况数据的接收端负责代码页的转换。例如,如果 DB2 客户端应用程序从联邦服务器数据获取数据,那么 DB2 的客户端进行转换代码页,反之亦然。
代码页转换依赖于远端数据源客户端 / 服务器
对于那些功能强大的数据源,例如 DB2 和 Oracle,由于它们的客户端本身具有就拥有强大的代码页转换能力,因此我们只需要了解如何将它们的代码页转换配置设置到联邦数据库下。通常遵循以下准则:
对于远端数据源,联邦数据库扮演了该数据源的客户端应用程序的角色。包装器通过客户端 API 将联邦数据库和远程数据源连接起来。
根据相应的包装器,需要将远端数据源代码页转换的相关环境变量或系统参数设置到 db2dj.ini 文件中。
联邦数据库与远端数据源连接建立时,db2dj.ini 中的代码页环境变量将被设置在当前会话中。
下面我们以 Oracle 包装器为例,进行详细的说明:
图 2. Oracle 包装器代码页转换框架
查看原图(大图)
Oracle 包装器的代码页转换依赖于 Oracle 数据源。为了更好的理解联邦服务器 Oracle 包装器如何处理代码页转换,我们最好先了解下 Oracle 数据源处理代码页转换的机制。
Oracle 使用 NLS 参数来指定其服务器和客户端的行为约定。大约有 20 个 NLS 参数影响不同的 NLS 域,比如 NLS_LANG,NLS_DATE_FORMAT,NLS_TIMESTAMP_FORMAT,NLS_CURRENCY 等等。
共有四种设置 NLS 参数的方法,优先级依次增高
服务器端设置初始化参数
客户端设置服务器变量
使用 ALTER SESSION 语句指定
使用 SQL 函数指定
Oracle 客户端的环境变量 NLS_LANG 是联邦数据库中所使用的最重要的 NLS 参数。NLS_LANG 通过下面的语法指定语言、区域和代码字符集。
NLS_LANG = language_territory.charset
从 Oracle 的角度来看,联邦数据库更像 Oracle 的一个客户端应用程序,因此设置 Oracle 客户端环境变量 NLS_LANG 是利用 Oracle 进行联邦数据库和 Oracle 服务器之间的代码页转换前提条件。
联邦数据库 Oracle 包装器可以在配置文件 db2dj.ini 里配置环境变量 NLS_LANG。定义的格式和上面一样,但是所指定的参数要根据联邦数据库的字符集、语言和区域设置来指定对应 Oracle 的参数。如果 NLS_LANG 没有在 db2dj.ini 里显性指定,Oracle 包装器在发起和 Oracle 服务器连接时,会根据联邦数据库的代码页来自动设置该参数。
例如,当联邦数据库的代码页是 GBK(1386)、区域代码是 CN,并且 Oracle 服务器数据库代码页是 UTF8,NLS_LANG 应该设置如下参数:
NLS_LANG=Simplified Chinese_China.ZHS16GBK.
NLS_LANG 语言和区域参数指定了其他的 NLS 参数的默认值,如日期时间的格式、数字格式的显示等等。您也可以设置这些 NLS 环境变量来改变的 NLS_LANG 对应的默认值。
联邦数据库 Oracle 封装器只使用一种日期 / 时间戳格式。在与远程 Oracle 数据源建立连接的时候,以下 Oracle 变量将被设置在当前会话中,这样 Oracle 服务器和客户端之间的日期 / 时间戳格式将依照这样的格式。因此,其他日期 / 时间戳格式的数据将不能被正确处理,比如'20-MAY-2009'。
NLS_TIMESTAMP_FORMAT=YYYY-MM-DD HH24:MI:SS.FF
NLS_DATE_FORMAT=YYYY-MM-DD HH24:MI:SS
代码页转换依赖于联邦数据库包装器
联邦数据库可以帮助那些自身不能提供或者只能部分提供代码页转换的远端数据源完成相应转换。例如,对于有表结构特征的文本文件,显然数据源没有代码页转换的能力,此时联邦数据库中的 File 包装器可以通过设置昵称选项 CODEPAGE,提供不同代码页的转换。
复杂情况下的代码页转换
联邦数据库某些包装器如 ODBC 包装器,可以提供更为灵活的代码页转换配置。下面,我们以 ODBC 包装器为例进行说明。
ODBC 包装器可以联邦所有支持 ODBC 3.0 标准的数据源,因此,ODBC 包装器可以联邦那些本身功能强大的数据源如 PostgreSQL,也可以联邦一些非关系型数据源如文本文件。所以数据代码页转换还依赖于 ODBC 驱动管理器、远端数据源提供的 ODBC 驱动、远端数据源客户端以及服务器。例如,图 4 展示了三种不同数据源 Classic Federation Server、Microsoft SQL Server 和 PostgreSQL 数据源。
图 3. ODBC 包装器的代码页转换框架
查看原图(大图)
假设 DB2 的客户端应用程序通过昵称从远端数据源获取数据。我们需要考虑以下四个部分:
远端数据源服务器到其客户端:例如对于 PostgreSQL,您可以在其服务器端设置参数来控制其客户端和服务器的代码页转换规则。
客户端到数据源 ODBC 驱动:例如对于主机 Classic Federation Server,可以在客户端 ODBC 配置文件中设置客户机和服务器的代码页转换规则。
数据源 ODBC 驱动到 ODBC 驱动管理器:例如对于 IBM Branded 的 DataDirect ODBC 驱动管理器,当使用 Unicode 应用程序和 ASCII 的 ODBC 驱动或者,使用 ASCII 应用程序和 Unicode 的 ODBC 驱动时,它可以提供与驱动之间的代码页转换规则。如果使用 IBM Branded 的 DataDirect ODBC 驱动程序管理器,请到 DataDirect 网站上了解参数 IANAAppCodePage 更多细节。
ODBC 驱动管理器到 ODBC 包装器:ODBC 包装器来对于那些无法进行代码页转换的 ODBC 驱动程序管理器或驱动器进行补偿。
代码页转换依赖于 DB2 应用程序客户端
联邦数据库可以对异构数据源提供透明访问,客户端应用程序不用考虑在整个过程中进行了几次代码页的转换。通常 DB2 客户端应用程序的代码页依赖于客户端操作系统的 locale。但是,对于一些情况,在应用程序代码页和系统 locale 不兼容时,需要设置变量 DB2CODEPAGE。除此之外,对于 Java™ 应用程序,也需要一些特殊的配置。下面我们以 DB2 控制中心为例进行阐述。
在使用 DB2 的控制中心界面时,在数据源服务器、客户端和联邦数据库包装器的代码页都设置正确的前提下,仍然不能正确显示 Unicode 字符如中文和日文。这些字符可以显示在命令行窗口,但是却不能在控制中心界面上显示。为了深入了解此类问题,我们先介绍一些 Java 应用程序相关的编码设置技术。
Java 虚拟机默认编码格式
JVM 的每个实例都有一个默认的字符编码格式,这个默认的编码在虚拟机启动的时候确定,并且一般会依赖于操作系统使用的 locale 和 encoding,我们可以通过下面这个命令查看 JVM 默认的编码格式 encoding=System.getProperty(“file.encoding”)
Java 以系统默认编码读入源文件,然后按 Unicode 进行编码。Java 运行时,也采用 Unicode 编码,为了提高内存空间利用效率对 Unicode 字符编码采用了 UTF-8 的方式编码,并且默认输入和输出的都是操作系统的默认编码。
当数据需要被以一种不同于 file.encoding 中的编码格式读入或者读出 Java 程序的时候,可以使用 Java IO 类,比如 java.io.InputStreamReader, java.io.FileReader, java.io.OutputStreamReader, java.io.FileWriter,java.lang.String. 它们支持在其中声明一个编码格式的值来替代现在 JVM 中使用的默认编码格式的值。下面的代码给出了一个简单的例子:
清单 1. Java 类替代虚拟机默认编码格式
System.out.println("File.encoding"=System.getProperty("file.encoding"));
stmt = conn.createStatement();
ResultSet resultSet = stmt.executeQuery("SELECT * FROM my_table");
while (resultSet.next()) {
String s = resultSet.getString(1);
System.out.println(s);
//the hex value of s
String hexs="";
byte[] bytes = s.getBytes();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(bytes[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
hexs += hex.toUpperCase();
}//for
System.out.println(hexs);
} //while
Java 应用与数据库
在下面的代码段中,我们给出了一个用 Java 来访问数据库的例子:
清单 2. Java 类访问数据库时的代码页转换
System.out.println(System.getProperty("file.encoding"));
stmt = conn.createStatement();
ResultSet resultSet = stmt.executeQuery("SELECT * FROM my_table");
while (resultSet.next()) {
String s = resultSet.getString(1);
System.out.println(s);
//the hex value of s
String hexs="";
byte[] bytes = s.getBytes();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(bytes[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
hexs += hex.toUpperCase();
}//for
System.out.println(hexs);
} //while
在不同的编码格式下,它的输出显示如下:
表 1. 同一程序在不同虚拟机上运行的不同结果
输出 1 | 输出 2 |
GB18030 | ISO8859-1 |
北京 | ?? |
B1B1BEA9(北京的 GBK 编码) | 3F3F2020 (乱码) |
中国 | ?? |
D6D0B9FA(中国的 GBK 编码) | 3F3F2020(乱码) |
如果默认的虚拟机编码是 GB18030,Java 程序会将从数据库读入的数据按 GBK 进行编码。如果默认的虚拟机编码是 ISO8859-1,数据将会被先转换为 ISO8859-1 编码,然后从 ISO8859-1 转换到 Java 内部编码 Unicode。但是编码 ISO8859-1 不支持中文,因此会显示乱码。
如果操作系统编码是 ISO8859-1,从数据库中取出的中文字符就没法转换成操作系统编码格式下的二进制,从而不能显示出结果来,解决方法是通过 db2set 命令设置 DB2 客户端的编码格式为 GBK。
解决 Java 应用程序的中日文等 Unicode 字符集显示的问题,一种方法是将 JVM 的默认编码格式改为支持中文或者日文的编码格式,如 GB18030,由于 JVM 的默认编码依赖于操作系统,这就需要将操作系统改为中文或者日文的操作系统。
在不改变操作系统 locale 等设置的情况下,我们还可以在编译和运行的时候加入编码格式参数来覆盖 JVM 默认编码格式的值。例如对于上面的 JVM 默认编码是 ISO8859-1,中文字符显示为乱码的程序,我们可以通过以下的方式编译和运行:
javac -encoding GB18030 testEncoding3.java
java -Dfile.encoding=GB18030 testEncoding3.class
运行结果可以看到程序中的编码格式已经被我们在运行时设置的参数覆盖:
清单 3. JVM 编码改变后的结果输出
GB18030
北京
B1B1BEA9
中国
D6D0B9FA
Java 应用与联邦数据库
Java 应用直接访问远端数据源中的数据与从联邦数据库中通过别名访问远端数据有什么区别呢?我们可以通过下面的代码段来进行比较
清单 4. Java 类访问联邦数据库时的代码页转换
System.out.println(System.getProperty("file.encoding"));
stmt = conn.createStatement();
ResultSet resultSet = stmt.executeQuery("SELECT hex(col) FROM my_nick");
while (resultSet.next()) {
String s = resultSet.getString(1);
System.out.println(s);
}//for
System.out.println(hexs);
} //while
假设联邦数据库编码为 GBK,远端数据源编码为 UTF8。联邦数据库建立一个基于远端数据源表“my_table”的昵称“my_nick”。用这个测试脚本,可以看到从昵称读到的字节编码是 GBK,而从远端数据表中读到的字节编码为 UTF8。所以当 Java 应用程序访问通过联邦数据库访问远端表时,并不关心远端数据源的代码页。在这种情况下,它会先将数据从 GBK 编码转换为 JVM 的编码,然后 JVM 会转换其编码为 Unicode。
包装器代码页配置汇总表
由于篇幅限制,我们不能对所有的包装进行更详细的介绍,表 2 提供一个代码页转换参数汇总表便于读者参考。
表 2. 配置汇总表
包装器 | 配置选项 | 说明 |
ODBC | CODEPAGE | 设置服务器选项 CODEPAGE,指定 ODBC 客户端的代码页。另外,可以根据此数据源的文档设置数据源特有的代码页转换配置。 |
Microsoft SQL Server | CODEPAGE | 设置服务器选项 CODEPAGE,指定 Microsoft SQL Server 客户端的代码页。 |
Oracle | NLS_LANG | 如果在 db2dj.ini 文件中设置 NLS_LANG 环境变量 , 包装器会使用该代码页。如果没有设置 NLS_LANG,包装器会根据 DB2 的区域代码和代码页选择对应的 Oracle 地区代码进行设置。若没有对应的代码页,Oracle 包装器会默认 NLS_LANG 的值为 American_America.US7ASCII。 |
Teradata | TERADATA_CHARSET | 如果在 db2dj.ini 文件中设置 TERADARA_CHARSET 环境变量 , 包装器会使用该代码页。若该值不是有效值,Teradata 包装器会返回错误。如果没有设置 TERADARA_CHARSET,包装器会根据 Teradata 客户端字符集设置代码页。 |
Sybase | SYBASE_CHARSET | 如果在 db2dj.ini 文件中设置 SYBASE_CHARSET 环境变量 , 包装器会使用该代码页,你可以在 $SYBASE\charsets 目录下查看所有 Sybase 支持的字符集。若该值不是有效值,Teradata 包装器会返回错误。如果没有设置 SYBASE_CHARSET,包装器会根据 DB2 字符集相对应的 Sybase 字符集设置代码页。若没有相对应的代码页,Sybase 包装器会默认 iso_1 字符集。 |
Informix | CLIENT_LOCALE DB_LOCALE DBNLS | 下列三个变量需要如果设置在联邦数据库 db2dj.ini 文件中,包装器会使用该代码页: CLIENT_LOCALE 指定 Informix 的 locale,如果没有设置 CLIENT_LOCALE,包装器会根据 DB2 的区域代码和代码页选择对应的 Informix 地区代码进行设置。若没有对应的代码页,Informix 包装器在 UNIX®平台会默认 en_us.8859-1,而在 Windows®平台上默认为 en_us.CP1252。 DB_LOCALE 该环境变量用来指定 Informix 数据库的代码页。使用后 Informix 将会按照 Infomix 数据库和客户端的代码也进行转换。 DBNLS 指定 Informix 是否需要验证环境变量 DB_LOCALE 的值与 Informix 数据库的实际区域代码吻合。 |
Flat File | CODEPAGE | 昵称选项,用来设置文本文件和联邦数据库代码页转换。 |
Control Center | Font.properties | font.properties 配置文件用来设置 Java 和操作系统字体映射 |
常见问题及解决办法
为什么在使用 DB2 命令行窗口连接 GBK 代码页的数据库时遇到 SQL0332N 错误?
该错误的原因是,用户所在客户端的代码页为 ISO8859-1。解决办法可以有两种:设置 DB2CODEPAGE 为 GBK 或者不设置 DB2CODEPAGE 的前提下改变操作系统的 locale 为支持中文。
为什么在使用 DB2 控制中心时,中文显示的是乱码?
原因是用户没有正确设置从 java 逻辑字体到物理字体的映射,或者该物理字体没有被安装。
为什么在使用代码页为 GBK 的联邦数据库去联邦远端代码页为 UTF8 的 Oracle 数据库时,显示乱码?
联邦数据库的代码页为 GBK,Oracle 数据库的代码页为 UTF8。在 Oracle 数据库中定义 Table T1(C1 varchar(10)) 并插入 “中国”“北京”两行记录。清单 5 展示了表 T1 的内容和其 UTF8 编码。
清单 5. Oracle 服务器中表 T1 的数据和 UTF8 编码
SQL> select * from t1;
C1
----------
中国
北京
SQL> select dump(C1,16) from t1;
DUMP (C1,16)
---------------------------------------
Typ=1 Len=6: e4,b8,ad,e5,9b,bd
Typ=1 Len=6: e5,8c,97,e4,ba,ac
设置 NLS_LANG=Simplified Chinese_China.UTF8。通“select userenv('language') from dual”语句获取 Oracle NLS 参数的值, 并将该值设置到 db2dj.ini 文件中,NLS_LANG=Simplified Chinese_China.UTF8。
清单 6. Oracle 的 NLS 参数值
SQL> select userenv('language') from dual;
USERENV('LANGUAGE')
----------------------------------------------
SIMPLIFIED CHINESE_CHINA.UTF8
在联邦数据库端查询后,结果显示为乱码
从联邦数据库中查询昵称,得了错误编码的数据。因为 NLS_LANG 在配置文件 db2dj.ini 中设置成 UTF8 编码,并且 Oracle 的客户端 和服务器之间并无代码页转换,所以联邦数据库获得的中文字符并没有做代码页转换,从而不能显示期望的结果。
清单 7. 设置 NLS_LANG= Simplified Chinese_China.UTF8 时,联邦数据库昵称的查询结果
=> db2 "select * from N1"
C1
----------
涓 浗
鍖椾含
2 record(s) selected.
=> db2 "select hex(C1) from N1"
1
--------------------
E4B8ADE59BBD
E58C97E4BAAC
2 record(s) selected.
改变 db2dj.ini 中参数 NLS_LANG 的设置为 GBK,并重新启动 DB2,Oracle 服务器的 UTF8 字符将会被正确的转换成 GBK 字符编码。
清单 8. 设置 NLS_LANG= Simplified Chinese_China.ZHS16GBK 时,联邦数据库昵称的查询结果
=> db2 "select * from N1"
C1
----------
中国
北京
2 record(s) selected.
=> db2 "select hex(C1) from N1"
1
--------------------
D6D0B9FA
B1B1BEA9
2 record(s) selected.
综述所说,在 db2dj.ini 应该设置正确的 NLS_LANG 参数,因为该参数是 Oracle 的必要参数,并且其值应该遵循 Oracle 的 NLS_LANG 的格式。
结论
本文主要解释了联邦数据库如何很好的支持多国语言和代码页。基于一些实际用户应用时遇到的问题,我们根据用户不同的环境和场景提供了多种灵活的解决方法。根据本文,用户可以更为容易的使用联邦数据库和异构数据源。
更多精彩
赞助商链接