java.sql.SQLException: Valor de string incorreto: '\ xF0 \ x9F \ x91 \ xBD \ xF0 \ x9F…'

107

Tenho o seguinte valor de string: "walmart obama 👽💔"

Estou usando MySQL e Java.

Estou recebendo a seguinte exceção: `java.sql.SQLException: Valor de string incorreto: '\ xF0 \ x9F \ x91 \ xBD \ xF0 \ x9F ...'

Aqui está a variável que estou tentando inserir:

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

Meu código Java que está tentando inserir "walmart obama 👽💔" é uma declaração preparada. Portanto, estou usando o setString()método.

Parece que o problema é a codificação dos valores 👽💔. Como posso consertar isso? Anteriormente, eu estava usando Derby SQL e os valores 👽💔 acabaram sendo dois quadrados (acho que esta é a representação do caractere nulo)

Toda ajuda é bem-vinda!

CodeKingPlusPlus
fonte
Parece uma duplicata de stackoverflow.com/questions/10957238/…
Joshua Davis
Ao criar o banco de dados, você pode fornecer o conjunto de caracteres e agrupamento desta forma:CREATE DATABASE db_name CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Max Peng

Respostas:

145

O que você tem EXTRATERRESTRIAL ALIEN (U+1F47D)e o BROKEN HEART (U+1F494)que não está no plano multilíngue básico. Eles não podem ser representados em java como um char "👽💔".length() == 4,. Eles definitivamente não são caracteres nulos e você verá quadrados se você não estiver usando fontes que os suportem.

MySQL de utf8apenas suporta avião multilingual básico, e você precisa usar utf8mb4em vez :

Para um caractere suplementar, utf8 não pode armazenar o caractere, enquanto utf8mb4 requer quatro bytes para armazená-lo. Como o utf8 não pode armazenar o caractere, você não tem nenhum caractere suplementar nas colunas do utf8 e não precisa se preocupar com a conversão de caracteres ou perda de dados ao atualizar os dados do utf8 de versões anteriores do MySQL.

Portanto, para suportar esses caracteres, seu MySQL precisa ser 5.5+ e você precisa usar em utf8mb4qualquer lugar. A codificação da conexão precisa ser utf8mb4, o conjunto de caracteres precisa ser utf8mb4e a colação precisa ser utf8mb4. Para java ainda é justo "utf-8", mas o MySQL precisa de uma distinção.

Não sei qual driver você está usando, mas uma maneira agnóstica de driver para definir o conjunto de caracteres de conexão é enviar a consulta:

SET NAMES 'utf8mb4'

Logo após fazer a conexão.

Veja também para o Conector / J :

14.14: Como posso usar UTF8 de 4 bytes, utf8mb4 com Conector / J?

Para usar UTF8 de 4 bytes com Conector / J, configure o servidor MySQL com character_set_server = utf8mb4. O conector / J usará essa configuração , desde que characterEncoding não tenha sido definido na string de conexão . Isso é equivalente à autodetecção do conjunto de caracteres.

Ajuste suas colunas e banco de dados também:

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

Novamente, sua versão do MySQL precisa ser relativamente atualizada para suporte utf8mb4.

Esailija
fonte
Confira minha outra postagem relacionada: stackoverflow.com/questions/13748170/… . Se você puder responder, então você também terá respondido a esta pergunta. O outro post tem mais detalhes do que fiz.
CodeKingPlusPlus
1
@CodeKingPlusPlus mudou tudo em seu banco de dados para utf8mb4, parece que ainda está usando utf8_general_ci..
Esailija
1
Não faça "SET NAMES" com Connector / J: dev.mysql.com/doc/connector-j/en/… Do not issue the query set names with Connector/J, as the driver will not detect that the character set has changed, and will continue to use the character set detected during the initial connection setup.
bcoughlan
1
Caso você queira apenas se livrar dos personagens de fora do BMP em vez de lidar com a bagunça de mudar seu banco de dados, veja aqui: stackoverflow.com/questions/4035562/…
Indigenuity
2
Eu tenho o mesmo problema, segui as etapas acima, mas não fui resolvido até alterar o character-set-server = utf8mb4 em C: \ ProgramData \ MySQL \ MySQL Server 5.7 \ my.ini
fattah.safa
16

Resumindo, para salvar símbolos que requerem 4 bytes, você precisa atualizar o conjunto de caracteres e o agrupamento para utf8mb4:

  1. tabela / coluna do banco de dados: alter table <some_table> convert to character set utf8mb4 collate utf8mb4_unicode_ci
  2. conexão do servidor de banco de dados ( ver )

No meu ambiente de desenvolvimento para o nº 2, prefiro definir parâmetros na linha de comando ao iniciar o servidor: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci


btw, preste atenção ao comportamento do Conector / J com SET NAMES 'utf8mb4':

Não emita os nomes dos conjuntos de consulta com o Conector / J, pois o driver não detectará que o conjunto de caracteres foi alterado e continuará a usar o conjunto de caracteres detectado durante a configuração da conexão inicial.

E evite definir o characterEncodingparâmetro no URL da conexão, pois isso substituirá a codificação do servidor configurada:

Para substituir a codificação detectada automaticamente no lado do cliente, use a propriedade characterEncoding na URL usada para se conectar ao servidor.

rilaby
fonte
15

Estranhamente, descobri que REMOVER &characterEncoding=UTF-8do JDBC urlfez o truque para mim com problemas semelhantes.

Com base em minhas propriedades,

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

Eu acho que isso suporta o que @Esailija disse acima, ou seja, meu MySQL, que é de fato 5.5, está descobrindo seu próprio sabor favorito de codificação UTF-8.

(Observe, também estou especificando InputStreamque estou lendo como UTF-8no código java, o que provavelmente não faz mal) ...

jsh
fonte
Talvez useUnicode=truenem seja necessário? No meu caso, a única coisa que funcionou foi definir character_set_server=utf8mb4globalmente no servidor (grupo de parâmetros RDS) e NÃO ter nenhum characterEncoding na URL JDBC.
Joshua Davis,
6

Como resolvi meu problema.

eu tinha

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

No meu URL de conexão jdbc hibernate, alterei o tipo de dados da string para texto longo no banco de dados, que antes era varchar.


fonte
Ótimo se você não precisa que a coluna seja indexada e é relativamente pequena, mas posso fazer este truque para todas as minhas colunas
shareef
3

Anexe a linha useUnicode=true&amp;characterEncoding=UTF-8ao seu url jdbc.

No seu caso, os dados não estão sendo enviados por meio de UTF-8codificação.

JHS
fonte
Como faço para anexar isso? Na minha string de conexão? Estou usando o Netbeans, se isso ajudar.
CodeKingPlusPlus
Como você está criando a conexão?
JHS de
DriverManager.getConnection ("jdbc: mysql: // localhost: #### / [dbName]", [nome de usuário], [senha]);
CodeKingPlusPlus
Faça assim - DriverManager.getConnection ("jdbc: mysql: // localhost: #### / [dbName]? UseUnicode = true & amp; characterEncoding = UTF-8", [nome do usuário], [senha]);
JHS
1
Raspe isso, esqueci o '?' Mas agora estou de volta ao mesmo erro da postagem original ...
CodeKingPlusPlus
3

Enfrentei o mesmo problema e resolvi definindo o agrupamento como utf8_general_ci para cada coluna.

Appy
fonte
2

Eu acho que o MySQL não acredita que este seja um texto UTF8 válido. Eu tentei uma inserção em uma tabela de teste com a mesma definição de coluna (a conexão do cliente mysql também era UTF8) e embora tenha feito a inserção, os dados que eu recuperei com o cliente MySQL CLI, bem como JDBC, não recuperaram os valores corretamente. Para ter certeza de que o UTF8 funcionou corretamente, inseri um "ö" em vez de um "o" para 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

Pequeno aplicativo java para testar:

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

}

Resultado:

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

Além disso, tentei a mesma inserção com a conexão JDBC e gerou a mesma exceção que você está obtendo. Acredito que seja um bug do MySQL. Talvez já haja um relatório de bug sobre tal situação ..

Friek
fonte
A propósito, os caracteres em sua string nem aparecem corretamente no Firefox e no Chrome no OSX. Eles aparecem corretamente em meu aplicativo iTerm. Eu acho que isso depende da fonte.
Friek
1

Eu tive o mesmo problema e depois de ir cuidadosamente contra todos os conjuntos de caracteres e descobrir que eles estavam corretos, percebi que a propriedade bugada que eu tinha em minha classe estava anotada como @Column em vez de @JoinColumn (javax.presistence; hibernate) e estava quebrando tudo.

Jon
fonte
1

executar

show VARIABLES like "%char%”;

encontre o servidor de conjunto de caracteres se não for utf8mb4.

configure-o no seu my.cnf, como

vim /etc/my.cnf

adicione uma linha

character_set_server = utf8mb4

finalmente reinicie o mysql

Kevin hawk
fonte
1
character_set_serveré a opção, NÃOcharacter-set-server
Arun SR
0

Essa configuração useOldUTF8Behavior = true funcionou bem para mim. Não deu erros de string incorretos, mas converteu caracteres especiais como à em vários caracteres e salvou no banco de dados.

Para evitar tais situações, removi esta propriedade do parâmetro JDBC e, em vez disso, converti o tipo de dados da minha coluna em BLOB. Isso funcionou perfeitamente.

Prithu Kumar
fonte
Você poderia adicionar mais detalhes à sua resposta? (código, comandos, etc.)
aBnormaLz
-2

Além disso, o tipo de dados pode usar a instalação de blob de varchar ou texto.

barry xu
fonte
Você não quer isso
ECostello