Oracle JDBC 瘦驱动程序:编码问题
Oracle JDBC Thin driver : encoding issues
我正在尝试从 Oracle 数据库中声明的虚拟函数中检索一些数据。
函数是这样创建的 SQL 开发者:
create or replace NONEDITIONABLE FUNCTION hello
RETURN varchar2 is
BEGIN
return 'Voici les caractères accentués : àéôï etc...' || chr(10) || 'ça marche ?';
END;
我的 Java 应用程序目前在 NetBeans 中执行,如下所示:
try(Connection connection = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:OraDoc", "sys as sysdba", "MyPasswd123")) {
CallableStatement cs = connection.prepareCall("{? = call hello}");
cs.registerOutParameter(1, Types.NVARCHAR);
cs.execute();
byte[] bytes = cs.getBytes(1);
logger.info(Hex.encodeHexString(bytes));
logger.info(new String(bytes, StandardCharsets.UTF_8));
logger.info(new String(bytes, StandardCharsets.UTF_16));
logger.info(new String(bytes, StandardCharsets.ISO_8859_1));
logger.info(new String(bytes, StandardCharsets.US_ASCII));
} catch(SQLException e) {
logger.error(e.getMessage(), e);
return;
}
输出如下所示:
2021-07-20 13:24:36 INFO Main:119 - 0056006f0069006300690020006c00650073002000630061007200610063007400e800720065007300200061006300630065006e0074007500e900730020003a002000e000e900f400ef0020006500740063002e002e002e000a00e700610020006d006100720063006800650020003f
2021-07-20 13:24:36 INFO Main:121 - Voici les caract?res accentu?s : ???? etc...
?a marche ?
2021-07-20 13:24:36 INFO Main:122 - Voici les caract?res accentu?s : ???? etc...
?a marche ?
2021-07-20 13:24:36 INFO Main:123 - Voici les caract?res accentu?s : ???? etc...
?a marche ?
2021-07-20 13:24:36 INFO Main:124 - Voici les caract?res accentu?s : ???? etc...
?a ma
使用相同的连接,我 运行 两个不同的查询试图理解这个问题:
SELECT * FROM v$nls_parameters WHERE parameter LIKE '%CHARACTERSET';
SELECT * FROM nls_session_parameters;
产生以下内容(截断):
2021-07-20 13:24:36 INFO Main:51 - PARAMETER -> NLS_CHARACTERSET
2021-07-20 13:24:36 INFO Main:51 - VALUE -> AL32UTF8
2021-07-20 13:24:36 INFO Main:51 - CON_ID -> 0
2021-07-20 13:24:36 INFO Main:51 - PARAMETER -> NLS_NCHAR_CHARACTERSET
2021-07-20 13:24:36 INFO Main:51 - VALUE -> AL16UTF16
2021-07-20 13:24:36 INFO Main:51 - CON_ID -> 0
2021-07-20 13:24:36 INFO Main:55 - ==============================================================
2021-07-20 13:24:36 INFO Main:63 - PARAMETER -> NLS_LANGUAGE
2021-07-20 13:24:36 INFO Main:63 - VALUE -> AMERICAN
2021-07-20 13:24:36 INFO Main:63 - PARAMETER -> NLS_TERRITORY
2021-07-20 13:24:36 INFO Main:63 - VALUE -> AMERICA
我尝试过不同的方法,例如:
- 在连接字符串的末尾添加“?useUnicode=true&characterEncoding=utf-8”,就像我连接到 MySQL 数据库时所做的那样
- 将 NLS_LANG 更改为 NLS_LANG=AMERICAN_AMERICA.UTF8 每 How to setup the NLS_LANG Properly for UNIX
- 注册输出参数时使用 NVARCHAR 而不是 VARCHAR
- 添加参数-Doracle.jdbc.defaultNChar=true
但没有改善。
供参考,这是我在该项目中唯一的两个依赖项(与 Oracle 相关):
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc11</artifactId>
<version>21.1.0.0</version>
</dependency>
<dependency>
<groupId>com.oracle.ojdbc</groupId>
<artifactId>orai18n</artifactId>
<version>19.3.0.0</version>
</dependency>
- 我删除了 NLS_LANG 环境变量(感谢@Wernfried Domscheit)
- 注册输出参数 a
时,我已切换回 VARCHAR 而不是 NVARCHAR
- 我正在使用 getString()(我正在使用 getBytes() 来尝试查看我是否收到了 ? 个字符或者是否是显示问题。
- 我尝试使用 UTF_16BE 字符集
创建字符串
结果还是一样:
try(Connection connection = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:OraDoc?useUnicode=true&characterEncoding=utf-8", "sys as sysdba", "MyPasswd123")) {
CallableStatement cs = connection.prepareCall("{? = call hello}");
cs.registerOutParameter(1, Types.VARCHAR);
cs.execute();
logger.info(cs.getString(1));
} catch(SQLException e) {
logger.error(e.getMessage(), e);
return;
}
2021-07-20 17:00:20 INFO Main:43 - Voici les caract?res accentu?s : ???? etc...
我错过了什么?
首先,你的函数定义为RETURN varchar2
,而不是nvarchar2
,但你在这里使用的是cs.registerOutParameter(1, Types.NVARCHAR);
。并且不要使用 cs.getBytes(1)
作为字符串,使用 getString()
代替
感谢@SayanMalakshinov,我意识到这是 netbeans 配置错误。
我在项目的 nbactions.xml 文件部分添加了 <Env.JAVA_TOOL_OPTIONS>-Dfile.encoding=UTF8</Env.JAVA_TOOL_OPTIONS>
,它立即生效。
我正在尝试从 Oracle 数据库中声明的虚拟函数中检索一些数据。
函数是这样创建的 SQL 开发者:
create or replace NONEDITIONABLE FUNCTION hello
RETURN varchar2 is
BEGIN
return 'Voici les caractères accentués : àéôï etc...' || chr(10) || 'ça marche ?';
END;
我的 Java 应用程序目前在 NetBeans 中执行,如下所示:
try(Connection connection = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:OraDoc", "sys as sysdba", "MyPasswd123")) {
CallableStatement cs = connection.prepareCall("{? = call hello}");
cs.registerOutParameter(1, Types.NVARCHAR);
cs.execute();
byte[] bytes = cs.getBytes(1);
logger.info(Hex.encodeHexString(bytes));
logger.info(new String(bytes, StandardCharsets.UTF_8));
logger.info(new String(bytes, StandardCharsets.UTF_16));
logger.info(new String(bytes, StandardCharsets.ISO_8859_1));
logger.info(new String(bytes, StandardCharsets.US_ASCII));
} catch(SQLException e) {
logger.error(e.getMessage(), e);
return;
}
输出如下所示:
2021-07-20 13:24:36 INFO Main:119 - 0056006f0069006300690020006c00650073002000630061007200610063007400e800720065007300200061006300630065006e0074007500e900730020003a002000e000e900f400ef0020006500740063002e002e002e000a00e700610020006d006100720063006800650020003f
2021-07-20 13:24:36 INFO Main:121 - Voici les caract?res accentu?s : ???? etc...
?a marche ?
2021-07-20 13:24:36 INFO Main:122 - Voici les caract?res accentu?s : ???? etc...
?a marche ?
2021-07-20 13:24:36 INFO Main:123 - Voici les caract?res accentu?s : ???? etc...
?a marche ?
2021-07-20 13:24:36 INFO Main:124 - Voici les caract?res accentu?s : ???? etc...
?a ma
使用相同的连接,我 运行 两个不同的查询试图理解这个问题:
SELECT * FROM v$nls_parameters WHERE parameter LIKE '%CHARACTERSET';
SELECT * FROM nls_session_parameters;
产生以下内容(截断):
2021-07-20 13:24:36 INFO Main:51 - PARAMETER -> NLS_CHARACTERSET
2021-07-20 13:24:36 INFO Main:51 - VALUE -> AL32UTF8
2021-07-20 13:24:36 INFO Main:51 - CON_ID -> 0
2021-07-20 13:24:36 INFO Main:51 - PARAMETER -> NLS_NCHAR_CHARACTERSET
2021-07-20 13:24:36 INFO Main:51 - VALUE -> AL16UTF16
2021-07-20 13:24:36 INFO Main:51 - CON_ID -> 0
2021-07-20 13:24:36 INFO Main:55 - ==============================================================
2021-07-20 13:24:36 INFO Main:63 - PARAMETER -> NLS_LANGUAGE
2021-07-20 13:24:36 INFO Main:63 - VALUE -> AMERICAN
2021-07-20 13:24:36 INFO Main:63 - PARAMETER -> NLS_TERRITORY
2021-07-20 13:24:36 INFO Main:63 - VALUE -> AMERICA
我尝试过不同的方法,例如:
- 在连接字符串的末尾添加“?useUnicode=true&characterEncoding=utf-8”,就像我连接到 MySQL 数据库时所做的那样
- 将 NLS_LANG 更改为 NLS_LANG=AMERICAN_AMERICA.UTF8 每 How to setup the NLS_LANG Properly for UNIX
- 注册输出参数时使用 NVARCHAR 而不是 VARCHAR
- 添加参数-Doracle.jdbc.defaultNChar=true
但没有改善。
供参考,这是我在该项目中唯一的两个依赖项(与 Oracle 相关):
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc11</artifactId>
<version>21.1.0.0</version>
</dependency>
<dependency>
<groupId>com.oracle.ojdbc</groupId>
<artifactId>orai18n</artifactId>
<version>19.3.0.0</version>
</dependency>
- 我删除了 NLS_LANG 环境变量(感谢@Wernfried Domscheit)
- 注册输出参数 a 时,我已切换回 VARCHAR 而不是 NVARCHAR
- 我正在使用 getString()(我正在使用 getBytes() 来尝试查看我是否收到了 ? 个字符或者是否是显示问题。
- 我尝试使用 UTF_16BE 字符集 创建字符串
结果还是一样:
try(Connection connection = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:OraDoc?useUnicode=true&characterEncoding=utf-8", "sys as sysdba", "MyPasswd123")) {
CallableStatement cs = connection.prepareCall("{? = call hello}");
cs.registerOutParameter(1, Types.VARCHAR);
cs.execute();
logger.info(cs.getString(1));
} catch(SQLException e) {
logger.error(e.getMessage(), e);
return;
}
2021-07-20 17:00:20 INFO Main:43 - Voici les caract?res accentu?s : ???? etc...
我错过了什么?
首先,你的函数定义为RETURN varchar2
,而不是nvarchar2
,但你在这里使用的是cs.registerOutParameter(1, Types.NVARCHAR);
。并且不要使用 cs.getBytes(1)
作为字符串,使用 getString()
代替
感谢@SayanMalakshinov,我意识到这是 netbeans 配置错误。
我在项目的 nbactions.xml 文件部分添加了 <Env.JAVA_TOOL_OPTIONS>-Dfile.encoding=UTF8</Env.JAVA_TOOL_OPTIONS>
,它立即生效。