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();
            }
        });

    }
posted @ 2024-08-13 14:37  年迈的魔法师  阅读(15)  评论(0编辑  收藏  举报