【问题标题】:Java / Postgres / Mybatis - invalid byte sequence for encoding "UTF8": 0xe3 0xa1 0x54Java / Postgres / Mybatis - 编码“UTF8”的无效字节序列:0xe3 0xa1 0x54
【发布时间】:2021-05-26 10:21:04
【问题描述】:

在我们的应用服务器上,DevOps 团队在 Postgres (9.4) DB 中使用 SQL_ASCII 编码。

第 3 方应用程序正在将带有重音字符的姓氏插入到 Employee 表中,例如努涅斯

我的Java(8)应用程序是一个使用Mybatis(3.2.4)的Spring(4.3.15)WebApp

当我的应用程序从 SQL_ASCII 数据库中读取此类姓氏时,我得到:

org.postgresql.util.PSQLException:错误:编码“UTF8”的字节序列无效:0xe3 0xa1 0x54 在 org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2182) 在 org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1911) 在 org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:173) 在 org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:616) 在 org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:466) 在 org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:459) 在 org.apache.tomcat.dbcp.dbcp2.DelegatingPreparedStatement.execute(DelegatingPreparedStatement.java:93) 在 org.apache.tomcat.dbcp.dbcp2.DelegatingPreparedStatement.execute(DelegatingPreparedStatement.java:93) 在 jdk.internal.reflect.GeneratedMethodAccessor78.invoke(未知来源) 在 java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 在 java.base/java.lang.reflect.Method.invoke(Method.java:566) 在 org.apache.ibatis.logging.jdbc.PreparedStatementLogger.invoke(PreparedStatementLogger.java:55) 在 com.sun.proxy.$Proxy98.execute(未知来源)

如果我尝试通过以下方式更改 client_encoding:

SET client_encoding = 'SQL_ASCII';

然后我得到错误:

org.postgresql.util.PSQLException:服务器的 client_encoding 参数已更改为 LATIN1。 JDBC 驱动程序要求 client_encoding 为 UTF8 才能正确操作。 在 org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1950)

我怎样才能“安全地”从数据库中读取这些字符?

【问题讨论】:

    标签: java postgresql utf-8


    【解决方案1】:

    你迷路了。 SQL_ASCII 数据库不支持编码,它将所有字节(0 字节除外)视为平等。数据库中不会有编码转换。

    因此,除非数据被意外编码为 UTF-8,但事实并非如此(根据错误消息),否则您不能将其与 JDBC 驱动程序一起使用。

    您必须转储数据库并将其恢复(使用适当的-E 选项)到具有正确编码的不同数据库 (v13) 中。在此过程中,任何编码不一致都必须手动修复。

    This question 将提供更多见解。

    【讨论】:

    • 谢谢,但我有点困惑 - 关于 Postgres 如何认为无法读取除 UTF-8 以外的编码的任何数据库的限制是可以接受的?
    • 但是 PostgresJDBC 驱动坚持客户端编码是 UTF-8。如果数据库是 LATIN1 - 我们可以通过 PG JDBC 驱动程序读取它吗?
    • 是的,这样就可以了。这就是为什么我建议将数据转储并恢复到具有适当编码的数据库中。
    【解决方案2】:

    allowEncodingChanges=true

    您可以尝试在 JDBC 连接 URL 中设置 allowEncodingChanges=true 吗? (还有characterEncoding

    allowEncodingChanges = 布尔值

    当使用 V3 协议时,驱动程序会监控最终用户不应触及的某些服务器配置参数的变化。 client_encoding 设置由驱动程序设置,不应更改。如果驱动程序检测到更改,它将中止连接。但是,这种行为有一个合法的例外,即对驻留在服务器文件系统上的文件使用 COPY 命令。指定此文件编码的唯一方法是更改​​ client_encoding 设置。 JDBC 团队认为这是 COPY 命令的失败,并希望将来提供另一种指定编码的方法,但现在有这个 URL 参数。仅当您需要在进行复制时覆盖客户端编码时才启用此功能。

    参考:Chapter 3. Initializing the Driver

    错误消息中的字节为1110 00111010 00010101 0100

    如果存储的数据以ISO-8859-1 编码,则为ã¡T

    当这个字节流被读取为UTF-8时,第一个字节中的1110MSB表示3 UTF-8字节字符(自计数)。

    所以接下来的 2 个字节应该以 10MSB 开头。但是第 3rd 字节以 01MSB

    开头

    默认情况下,JDBC 驱动程序在 UTF-8 中解码此流,并在无效字节流上失败。

    假设第三个字节以 10MSB 开头,代码可以正常工作,但它会错误地将所有这些 3 字节映射到 single Unicode code point(假设原始编码不是UTF-8,并且对应的 unicode 代码点具有有效的字符表示)。

    【讨论】:

    • 这可能有效,但根据您引用的文本,这不是参数的预期目的,并且绝对符合“Kludge”的条件。没有一种认可的方法可以从 Java 中的 SQL_ASCII DB 中读取这些字符吗?
    • 另外,任何使用 MSB 为 1 的字节都留给 ASCII 解释。 When the server character set is SQL_ASCII, the server interprets byte values 0-127 according to the ASCII standard, while byte values 128-255 are taken as uninterpreted charactersmultibyte.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-06-19
    • 2019-04-09
    • 1970-01-01
    • 2016-11-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多