java.sql.SQLException: valor de cadena incorrecto: '\ xF0 \ x9F \ x91 \ xBD \ xF0 \ x9F…'

107

Tengo el siguiente valor de cadena: "walmart obama 👽💔"

Estoy usando MySQL y Java.

Recibo la siguiente excepción: `java.sql.SQLException: Valor de cadena incorrecto: '\ xF0 \ x9F \ x91 \ xBD \ xF0 \ x9F ...'

Aquí está la variable en la que estoy tratando de insertar:

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

Mi código Java que está intentando insertar "walmart obama 👽💔" es una declaración preparada. Entonces estoy usando el setString()método.

Parece que el problema es la codificación de los valores 👽💔. ¿Cómo puedo arreglar esto? Anteriormente estaba usando Derby SQL y los valores 👽💔 simplemente terminaron siendo dos cuadrados (creo que esta es la representación del carácter nulo)

¡Toda ayuda es muy apreciada!

CodeKingPlusPlus
fuente
Parece un duplicado de stackoverflow.com/questions/10957238/…
Joshua Davis
Cuando crea la base de datos, puede proporcionar el conjunto de caracteres y la colación de esta manera:CREATE DATABASE db_name CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Max Peng

Respuestas:

145

Lo que tienes es EXTRATERRESTRIAL ALIEN (U+1F47D)y lo BROKEN HEART (U+1F494)que no está en el plano multilingüe básico. Ni siquiera se pueden representar en Java como un solo carácter "👽💔".length() == 4. Definitivamente no son caracteres nulos y se verán cuadrados si no está utilizando fuentes que los admitan.

MySQL utf8solo admite el plano multilingüe básico, y debe usar utf8mb4en su lugar :

Para un carácter suplementario, utf8 no puede almacenar el carácter en absoluto, mientras que utf8mb4 requiere cuatro bytes para almacenarlo. Dado que utf8 no puede almacenar el carácter en absoluto, no tiene caracteres suplementarios en las columnas utf8 y no necesita preocuparse por convertir caracteres o perder datos al actualizar los datos utf8 de versiones anteriores de MySQL.

Entonces, para admitir estos caracteres, su MySQL debe ser 5.5+ y debe usarlo en utf8mb4todas partes. La codificación de conexión debe ser utf8mb4, el conjunto de caracteres debe ser utf8mb4y la recopilación debe ser utf8mb4. Para Java sigue siendo justo "utf-8", pero MySQL necesita una distinción.

No sé qué controlador está utilizando, pero una forma independiente del controlador de configurar el juego de caracteres de conexión es enviar la consulta:

SET NAMES 'utf8mb4'

Inmediatamente después de hacer la conexión.

Vea también esto para Connector / J :

14.14: ¿Cómo puedo usar UTF8, utf8mb4 de 4 bytes con Connector / J?

Para usar UTF8 de 4 bytes con Connector / J, configure el servidor MySQL con character_set_server = utf8mb4. El conector / J usará esa configuración siempre que no se haya configurado characterEncoding en la cadena de conexión . Esto es equivalente a la autodetección del juego de caracteres.

Ajuste sus columnas y base de datos también:

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

Nuevamente, su versión de MySQL debe estar relativamente actualizada para la compatibilidad con utf8mb4.

Esailija
fuente
Consulte mi otra publicación relacionada: stackoverflow.com/questions/13748170/… . Si puede responderla, también habrá respondido a esta pregunta. La otra publicación tiene más detalles de lo que he hecho.
CodeKingPlusPlus
1
@CodeKingPlusPlus ha cambiado todo en su base de datos a utf8mb4, parece que todavía está usando utf8_general_ci..
Esailija
1
No haga "nombres" SET con 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
En caso de que desee simplemente deshacerse de los caracteres de fuera del BMP en lugar de lidiar con el lío de cambiar su base de datos, consulte aquí: stackoverflow.com/questions/4035562/…
Indigenuity
2
Tengo el mismo problema, seguí los pasos anteriores, pero no se resolvieron hasta que cambié el character-set-server = utf8mb4 en C: \ ProgramData \ MySQL \ MySQL Server 5.7 \ my.ini
fattah.safa
16

En general, para guardar símbolos que requieren 4 bytes, debe actualizar el conjunto de caracteres y la colación para utf8mb4:

  1. tabla / columna de base de datos: alter table <some_table> convert to character set utf8mb4 collate utf8mb4_unicode_ci
  2. conexión al servidor de base de datos ( ver )

En mi entorno de desarrollo para el n. ° 2, prefiero establecer parámetros en la línea de comandos al iniciar el servidor: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci


Por cierto, preste atención al comportamiento del Conector / J con SET NAMES 'utf8mb4':

No emita los nombres del conjunto de consultas con Connector / J, ya que el controlador no detectará que el conjunto de caracteres ha cambiado y continuará utilizando el conjunto de caracteres detectado durante la configuración de la conexión inicial.

Y evite configurar el characterEncodingparámetro en la URL de conexión, ya que anulará la codificación del servidor configurado:

Para anular la codificación detectada automáticamente en el lado del cliente, use la propiedad characterEncoding en la URL utilizada para conectarse al servidor.

rilaby
fuente
15

Extrañamente, encontré que ELIMINAR &characterEncoding=UTF-8de la JDBC urlfuncionó para mí con problemas similares.

Basado en mis propiedades,

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

Creo que esto respalda lo que @Esailija ha dicho anteriormente, es decir, mi MySQL, que de hecho es 5.5, está descubriendo su propio sabor favorito de codificación UTF-8.

(Tenga en cuenta que también estoy especificando el InputStreamque estoy leyendo como UTF-8en el código java, lo que probablemente no duele) ...

jsh
fuente
¿Quizás useUnicode=trueni siquiera es necesario? En mi caso, lo único que funcionó es la configuración character_set_server=utf8mb4global en el servidor (grupo de parámetros RDS) y NO tener ninguna codificación de caracteres en la URL de JDBC.
Joshua Davis
6

Cómo resolví mi problema.

yo tenía

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

En mi url de conexión jdbc de hibernación y cambié el tipo de datos de cadena a texto largo en la base de datos, que antes era varchar.


fuente
Excelente si no necesita que la columna esté indexada y es relativamente pequeña, pero puedo hacer este truco para todas mis columnas
shareef
3

Agrega la línea useUnicode=true&amp;characterEncoding=UTF-8a tu url jdbc.

En su caso, los datos no se envían mediante UTF-8codificación.

JHS
fuente
¿Cómo agrego esto? ¿En mi cadena de conexión? Estoy usando Netbeans si eso ayuda.
CodeKingPlusPlus
¿Cómo estás creando la conexión?
JHS
DriverManager.getConnection ("jdbc: mysql: // localhost: #### / [dbName]", [nombre de usuario], [contraseña]);
CodeKingPlusPlus
Hágalo así: DriverManager.getConnection ("jdbc: mysql: // localhost: #### / [dbName]? UseUnicode = true & amp; characterEncoding = UTF-8", [nombre de usuario], [contraseña]);
JHS
1
Borra eso, me olvidé del '?' Pero ahora he vuelto al mismo error que en la publicación original ...
CodeKingPlusPlus
3

Me enfrenté al mismo problema y lo resolvió mediante el establecimiento de la intercalación de utf8_general_ci para cada columna.

Appy
fuente
2

Supongo que MySQL no cree que esto sea un texto UTF8 válido. Probé una inserción en una tabla de prueba con la misma definición de columna (la conexión del cliente mysql también era UTF8) y, aunque hizo la inserción, los datos que recuperé con el cliente CLI de MySQL y JDBC no recuperaron los valores correctamente. Para estar seguro de que UTF8 funcionó correctamente, inserté una "ö" en lugar de una "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

Pequeña aplicación java para probar con:

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

}

Salida:

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

Además, probé la misma inserción con la conexión JDBC y arrojó la misma excepción que está obteniendo. Creo que esto es un error de MySQL. Tal vez ya haya un informe de error sobre tal situación ...

Friek
fuente
Por cierto, los caracteres de su cadena ni siquiera se muestran correctamente en Firefox y Chrome en OSX. Aparecen correctamente en mi aplicación iTerm. Creo que esto depende de la fuente.
Friek
1

Tuve el mismo problema y después de ir con cuidado contra todos los conjuntos de caracteres y descubrir que estaban bien, me di cuenta de que la propiedad con errores que tenía en mi clase estaba anotada como @Column en lugar de @JoinColumn (javax.presistence; hibernate) y estaba rompiendo todo.

Jon
fuente
1

ejecutar

show VARIABLES like "%char%”;

busque el servidor de conjunto de caracteres si no es utf8mb4.

configúrelo en su my.cnf, como

vim /etc/my.cnf

agregar una línea

character_set_server = utf8mb4

por fin reiniciar mysql

Kevin Hawk
fuente
1
character_set_serveres la opción, NOcharacter-set-server
Arun SR
0

Esta configuración useOldUTF8Behavior = true funcionó bien para mí. No dio errores de cadena incorrectos, pero convirtió caracteres especiales como à en varios caracteres y los guardó en la base de datos.

Para evitar tales situaciones, eliminé esta propiedad del parámetro JDBC y en su lugar convertí el tipo de datos de mi columna a BLOB. Esto funcionó perfecto.

Prithu Kumar
fuente
¿Podría agregar más detalles a su respuesta? (código, comas, etc.)
aBnormaLz
-2

Además, el tipo de datos puede usar blob install de varchar o text.

barry xu
fuente
No quieres eso
ECostello