java.sql.SQLException: 字符串值不正确: '\xF0\x9F\x91\xBD\xF0\x9F...'

121

我有以下字符串值:“walmart obama”。

我正在使用MySQL和Java。

我遇到了以下异常:`java.sql.SQLException: Incorrect string value: '\xF0\x9F\x91\xBD\xF0\x9F...'

这是我试图插入的变量:

var1 varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL`

我正在尝试使用preparedStatement插入"walmart obama"的Java代码。因此,我正在使用setString()方法。

看起来问题在于值的编码。我该如何解决这个问题?以前我使用Derby SQL,但是值最终只变成了两个方块(我认为这是空字符的表示方式)

非常感谢任何帮助!


似乎是 https://dev59.com/qGgu5IYBdhLWcg3w8bhs#10959780 的重复问题。 - Joshua Davis
创建数据库时,您可以像这样指定字符集和排序规则:CREATE DATABASE db_name CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - Max Peng
11个回答

157
您拥有的是不在基本多语言平面中的 外星人符号 (U+1F47D)心碎符号 (U+1F494)。它们甚至无法在Java中表示为一个字符,"".length() == 4。它们绝对不是空字符,如果您没有使用支持它们的字体,会看到方块。 MySQL 的 utf8 只支持基本多语言平面,您需要使用 utf8mb4

对于补充字符,utf8 根本不能存储该字符,而 utf8mb4 需要四个字节才能存储。由于 utf8 根本不能存储该字符,因此在 utf8 列中没有任何补充字符,当您从旧版本的 MySQL 升级 utf8 数据时,您不需要担心转换字符或数据丢失。

因此,要支持这些字符,您的MySQL需为5.5+版本,并且需要在所有地方使用 utf8mb4。连接编码需要是 utf8mb4,字符集需要是 utf8mb4,排序规则需要是 utf8mb4。对于Java仍然是"utf-8",但MySQL需要有一个区分。
我不知道您正在使用哪个驱动程序,但设置连接字符集的跨驱动程序方式是发送查询:
SET NAMES 'utf8mb4'

连接建立后:

此外,对于Connector/J,也请参阅::

14.14:如何在Connector/J中使用4字节UTF8(utf8mb4)?

要在Connector/J中使用4字节UTF8,请使用character_set_server=utf8mb4配置MySQL服务器。 只要连接字符串没有设置characterEncoding,Connector/J就会使用该设置。这相当于自动检测字符集。

还需要调整您的数据库和列:

var1 varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL

需要注意的是,为了支持utf8mb4,你的MySQL版本需要相对较新。


请查看我的其他相关帖子:http://stackoverflow.com/questions/13748170/jdbc-mysql-collation-settings。如果你能回答它,那么你也会回答这个问题。另一个帖子有更多关于我所做的内容的细节。 - CodeKingPlusPlus
1
@CodeKingPlusPlus,你已经将数据库中的所有内容更改为utf8mb4了吗?看起来你仍在使用utf8_general_ci - Esailija
1
不要使用Connector/J进行“SET NAMES”操作:http://dev.mysql.com/doc/connector-j/en/connector-j-reference-charsets.html 不要使用Connector/J发出set names查询,因为驱动程序将无法检测到字符集的更改,并将继续使用在初始连接设置期间检测到的字符集。 - bcoughlan
2
如果您只想摆脱 BMP 外部的字符而不是处理更改数据库的混乱,请参见此处:https://dev59.com/YW865IYBdhLWcg3wA5uc - Indigenuity
2
我有同样的问题,按照上面的步骤操作后仍未解决,直到在C:\ProgramData\MySQL\MySQL Server 5.7\my.ini中更改了character-set-server=utf8mb4。 - fattah.safa
显示剩余3条评论

21

奇怪的是,我发现从JDBC url中移除&characterEncoding=UTF-8对于我遇到的类似问题起了作用。

根据我的属性,

jdbc_url=jdbc:mysql://localhost:3306/dbName?useUnicode=true

我认为这支持了@Esailija在上面所说的,也就是说,我的MySQL,确实是5.5,正在找出自己最喜欢的UTF-8编码方式。

(请注意,我在Java代码中也将我正在读取的InputStream指定为UTF-8,这可能也没有坏处)...


也许根本不需要使用 useUnicode=true?在我的情况下,唯一有效的方法是在服务器上全局设置 character_set_server=utf8mb4(RDS参数组),并且不要在JDBC URL中设置任何characterEncoding。 - Joshua Davis

17
总体来说,要保存需要4个字节的符号,您需要更新utf8mb4的字符集和排序规则:
  1. 数据库表/列: alter table <some_table> convert to character set utf8mb4 collate utf8mb4_unicode_ci
  2. 数据库服务器连接(参见

在我的开发环境中,对于第2点,我更喜欢在启动服务器时通过命令行设置参数: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci


顺便提一下,注意Connector/J与SET NAMES 'utf8mb4'的行为

不要使用Connector/J发出set names查询,因为驱动程序将无法检测到字符集已更改,而将继续使用在初始连接设置期间检测到的字符集。

避免在连接URL中设置characterEncoding参数,因为它会覆盖已配置的服务器编码:

为了在客户端上覆盖自动检测到的编码,请在用于连接到服务器的URL中使用characterEncoding属性。


7

我如何解决我的问题。

我遇到了

?useUnicode=true&amp;characterEncoding=UTF-8

在我的Hibernate JDBC连接URL中,我将数据库中的字符串数据类型更改为了longtext,而此前它是varchar。


如果您不需要该列被索引并且它相对较小,那就太好了,但我可以将这个技巧用于我所有的列。 - shareef

4

我曾经遇到同样的问题,并通过将每个列的Collation设置为utf8_general_ci来解决。


4

将行useUnicode=true&amp;characterEncoding=UTF-8添加到您的jdbc url中。

在您的情况下,数据未使用UTF-8编码发送。


我该如何将它附加到我的连接字符串中?如果有帮助的话,我正在使用Netbeans。 - CodeKingPlusPlus
你是如何创建连接的? - JHS
DriverManager.getConnection("jdbc:mysql://localhost:####/[dbName]", [用户名], [密码]); - CodeKingPlusPlus
像这样做 - DriverManager.getConnection("jdbc:mysql://localhost:####/[dbName]?useUnicode=true&characterEncoding=UTF-8", [用户名], [密码]); - JHS
1
取消上一次的修改,我忘记了“?”号。但现在我又回到了原始帖子中相同的错误... - CodeKingPlusPlus

2

我猜MySQL不认为这是有效的UTF8文本。我尝试在一个测试表中插入了相同列定义的数据(mysql客户端连接也是UTF8),虽然插入成功,但我用MySQL CLI客户端和JDBC检索到的数据并没有正确检索到值。为确保UTF8正常工作,我插入了一个“ö”而不是“o”代替obama:

johan@maiden:~$ mysql -vvv test < insert.sql 
--------------
insert into utf8_test values(_utf8 "walmart öbama ")
--------------

Query OK, 1 row affected, 1 warning (0.12 sec)

johan@maiden:~$ file insert.sql 
insert.sql: UTF-8 Unicode text

一个用于测试的小型Java应用程序:

package test.sql;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class Test
{

    public static void main(String[] args)
    {
        System.out.println("test string=" + "walmart öbama ");
        String url = "jdbc:mysql://hostname/test?useUnicode=true&characterEncoding=UTF-8";
        try
        {
            Class.forName("com.mysql.jdbc.Driver").newInstance();
            Connection c = DriverManager.getConnection(url, "username", "password");
            PreparedStatement p = c.prepareStatement("select * from utf8_test");
            p.execute();
            ResultSet rs = p.getResultSet();
            while (!rs.isLast())
            {
                rs.next();
                String retrieved = rs.getString(1);
                System.out.println("retrieved=\"" + retrieved + "\"");

            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

}

输出:

johan@appel:~/workspaces/java/javatest/bin$ java test.sql.Test
test string=walmart öbama 
retrieved="walmart öbama "

此外,我已经尝试使用JDBC连接进行相同的插入操作,但它抛出了与您遇到的相同异常。 我认为这是MySQL的一个bug。也许已经有关于这种情况的错误报告了。


顺便提一下,在你的字符串中,字符在OSX上的Firefox和Chrome浏览器中都无法正确显示。但是在我的iTerm应用程序中可以正确显示。我认为这取决于字体。 - Friek

1

我遇到了类似的问题,仔细检查了所有字符集,发现它们都没问题。后来我意识到,我的类中出现了错误的属性,它被注释为 @Column 而不是 @JoinColumn(javax.presistence; hibernate),导致一切都出了问题。


1
执行
show VARIABLES like "%char%”;

如果 character-set-server 不是 utf8mb4,请进行以下操作:

在 my.cnf 中设置它,如下所示:

vim /etc/my.cnf

添加一行

character_set_server = utf8mb4

最后重新启动mysql。

1
character_set_server is the option, NOT character-set-server - subject-q

0

这个设置 useOldUTF8Behavior=true 在我的情况下运行得很好。它没有给出任何不正确的字符串错误,但是它将像 Ã 这样的特殊字符转换为多个字符,并保存在数据库中。

为了避免这种情况,我从 JDBC 参数中移除了此属性,并将列的数据类型转换为 BLOB。这个方法非常完美。


请问您能否在您的回答中添加更多细节吗?(代码、注释等) - aBnormaLz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接