Oracle数据库US7ASCII字符集中文乱码
最近遇到一家客户的Oracle数据库,版本是11g,字符集是US7ASCII,当使用PL/SQL Developer工具插入和查询中文时都没问题,但是Java程序使用JDBC插入和查询中文时,中文乱码。
比如'a中文b'通过JDBC查询出来的乱码是这样的'aᅱᅫᅣb'
查询了一些资料,看到有网友通过这种方式解决了(只列出关键代码)。但是这个方法对于我的环境没有效果。
Properties properties = new Properties();
properties.setProperty("serverEncoding","ISO-8859-1");
properties.setProperty("clientEncoding","GBK");
String DBURL = "jdbc:wrap-jdbc:filters=encoding:jdbc:oracle:thin:@//100.100.100.100:1521/helowin";
理论上来说,US7ASCII是不能保存中文的,因为ASCII编码一个字符只占用一个字节,即8位,而且首位是0. 但是看PL/SQL Developer的确把中文查出来了,挺让人疑惑的(后面想可能是因为PL/SQL用的胖客户端,做了某些处理,而JDBC用的是瘦客户端)。
通过查看16进制,我们可以看到,数据是这样保存的,再用计算器算一下,我们可以看到,每个中文其实占用了两字节,每个字母占用了1字节,而且中文字节是1开头,挺像GBK编码方式的。
而Oracle驱动中,如果采用ResultSet的getString方法来获取值,会走到这么一段代码中去,这段代码获取到的char[]其实把每个中文拆分成了两个字符去处理,所以得到的结果是乱码,而且乱码部分的字符数是中文数的两倍。
经过测试发现采用ResultSet的getBytes(int columnIndex)方法先得到byte数组,再用new String转GBK可以得到正常的中文。
最终我的解决方法:
插入数据时,对可能存在中文的值进行一次转码
String newName = new String(textValue.getBytes("GBK"), "ISO-8859-1");
查询数据时,对于VARCHAR2和CHAR类型的列,从ResultSet中获取值时先用getBytes(int columnIndex)方法,然后再用new String(byte bytes[], String charsetName) 转为GBK
以下是我对NAME字段进行处理的例子。
private Connection getOracleConnection() throws Exception {
Class.forName("oracle.jdbc.OracleDriver");
Properties properties = new Properties();
properties.setProperty("user", "YourUser");
properties.setProperty("password", "YourPassword");
String url = "jdbc:oracle:thin:@//100.100.100.100:1521/helowin";
return DriverManager.getConnection(url,properties);
}
@Test
public void connSelect() throws Exception {
Connection connection = getOracleConnection();
PreparedStatement preparedStatement = connection.prepareStatement("select * from JDBI_TEST");
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet == null) {
return;
}
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
List<Map<String, Object>> list = new ArrayList<>();
while (resultSet.next()) {
Map<String, Object> map = new HashMap<>();
for (int i = 0; i < columnCount; i++) {
String columnName = metaData.getColumnName(i + 1);
if (!"NAME".equals(columnName)) {
continue;
}
byte[] bytes = resultSet.getBytes(i + 1);
map.put(columnName, bytes);
}
list.add(map);
}
list.forEach(map -> {
Object name = map.get("NAME");
try {
logger.info("name: {}, {}", Arrays.toString((byte[]) name), new String((byte[]) name, "GBK"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
});
}