web-dev-qa-db-fra.com

java.sql.SQLException: valeur chaîne incorrecte: '\ xF0\x9F\x91\xBD\xF0\x9F ...'

J'ai la valeur de chaîne suivante: "walmart obama ????????"

J'utilise MySQL et Java.

Je reçois l'exception suivante: `Java.sql.SQLException: valeur de chaîne incorrecte: '\ xF0\x9F\x91\xBD\xF0\x9F ...'

Voici la variable dans laquelle j'essaie d'insérer:

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

Mon code Java qui essaie d'insérer "walmart obama ????????" est un readyStatement. J'utilise donc la méthode setString().

Il semble que le problème soit l'encodage des valeurs ????????. Comment puis-je réparer cela? Auparavant, j'utilisais Derby SQL et les valeurs ???????? juste fini par être deux sqaures (je pense que ceci est la représentation du caractère nul)

Toute aide est grandement appréciée!

86
CodeKingPlusPlus

Ce que vous avez est EXTRATERRESTRIAL ALIEN (U+1F47D) et BROKEN HEART (U+1F494) qui Ne sont pas dans le plan multilingue de base. Ils ne peuvent même pas être représentés dans Java sous la forme d'un seul caractère, "????????".length() == 4. Ce ne sont certainement pas des caractères nuls et on verra des carrés si vous n'utilisez pas de polices les prenant en charge.

Le utf8 de MySQL ne supporte que le plan multilingue de base et vous devez utiliser utf8mb4 à la place :

Pour un caractère supplémentaire, utf8 ne peut pas stocker le caractère du tout, tandis que utf8mb4 nécessite quatre octets pour le stocker. Depuis utf8 ne peut pas stocker le caractère du tout, vous n'avez aucun caractère supplémentaire dans les colonnes utf8 et vous n'avez pas à vous soucier de la conversion des caractères ou perte de données lors de la mise à niveau des données utf8 à partir d'anciennes versions de MySQL.

Donc, pour supporter ces caractères, votre MySQL doit être 5.5+ et vous devez utiliser utf8mb4 partout. Le codage de la connexion doit être utf8mb4, le jeu de caractères doit être utf8mb4 et la collecte doit être utf8mb4. Pour Java, c'est toujours "utf-8", mais MySQL a besoin d'une distinction.

Je ne sais pas quel pilote vous utilisez, mais un moyen indépendant du pilote pour définir le jeu de caractères de connexion consiste à envoyer la requête:

SET NAMES 'utf8mb4'

Juste après avoir établi la connexion. 

Voir aussi ceci pour Connector/J :

14.14: Comment utiliser UTF8 à 4 octets, utf8mb4 avec Connector/J?

Pour utiliser UTF8 sur 4 octets avec Connector/J, configurez le serveur MySQL avec character_set_server = utf8mb4. Connector/J utilisera alors ce paramètre tant que characterEncoding a not été défini dans la connexion string. Cela équivaut à la détection automatique du jeu de caractères.

Ajustez également vos colonnes et votre base de données:

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

Encore une fois, votre version de MySQL doit être relativement à jour pour la prise en charge de utf8mb4.

121
Esailija

Bizarrement, j'ai constaté que RETIRER &characterEncoding=UTF-8 du JDBC url m'a permis de résoudre le problème avec des problèmes similaires.

Basé sur mes propriétés,

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

Je pense que cela va dans le sens de ce que @ Esailija a dit plus haut, à savoir que mon MySQL, qui est effectivement 5.5, est en train de définir sa propre version préférée du codage UTF-8.

(Remarque, je spécifie également la InputStream que je lis en tant que UTF-8 dans le code Java, ce qui ne fait probablement pas de mal) ...

14
jsh

En résumé, pour enregistrer des symboles nécessitant 4 octets, vous devez mettre à jour characher-set et le classement pour utf8mb4:

  1. table/colonne de la base de données: alter table <some_table> convert to character set utf8mb4 collate utf8mb4_unicode_ci
  2. connexion au serveur de base de données ( voir )

Sur mon environnement de développement pour # 2, je préfère définir les paramètres en ligne de commande lors du démarrage du serveur: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci


btw, faites attention à Comportement du connecteur/J avec SET NAMES 'utf8mb4':

N'émettez pas les noms de jeu de requêtes avec Connector/J, car le pilote ne détectera pas que le jeu de caractères a été modifié et continue d'utiliser le jeu de caractères détecté lors de l'établissement de la connexion initiale.

Et évitez de définir le paramètre characterEncoding dans l'URL de connexion, car celui-ci remplacera le codage du serveur configuré: 

Pour remplacer le codage automatiquement détecté côté client, utilisez la propriété characterEncoding dans l'URL utilisée pour se connecter au serveur.

12
rilaby

Comment j'ai résolu mon problème.

J'ai eu

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

Dans mon url de connexion hibernate jdbc et j'ai changé le type de données string en longtext dans la base de données, qui était varchar auparavant.

6
Indrek Ruubel

J'ai fait face au même problème et je l'ai résolu en définissant Collation sur utf8_general_ci pour chaque colonne.

3
Appy

Je suppose que MySQL ne croit pas que ce soit du texte UTF8 valide. J'ai essayé une insertion sur une table de test avec la même définition de colonne (la connexion client mysql était également UTF8) et bien que l'insertion ait été effectuée, les données que j'ai récupérées avec le client CLQL MySQL et JDBC n'ont pas extrait correctement les valeurs. Pour être sûr que UTF8 fonctionne correctement, j'ai inséré un "ö" à la place d'un "o" pour 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

Petite application Java à tester avec:

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

}

Sortie:

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

En outre, j'ai essayé le même insert avec la connexion JDBC et la même exception que vous obtenez est apparue .. Je crois qu'il s'agit d'un bogue MySQL. Peut-être y a-t-il déjà un rapport de bogue sur une telle situation ...

2
Friek

J'avais un peu le même problème et après avoir examiné avec soin tous les jeux de caractères et constaté qu'ils allaient bien, je me suis rendu compte que la propriété bugged que j'avais dans ma classe était annotée comme @Column au lieu de @JoinColumn (javax.presistence; hibernate) et c'était tout casser.

1
jon

execute

show VARIABLES like "%char%”;

find character-set-server si n'est pas utf8mb4.

le mettre dans votre my.cnf, comme

vim /etc/my.cnf

ajouter une ligne 

character-set-server = utf8mb4

au dernier redémarrage de mysql

1
Kevin Hawk

Ajoutez la ligne useUnicode=true&amp;characterEncoding=UTF-8 à votre URL jdbc.

Dans votre cas, les données ne sont pas envoyées en utilisant le codage UTF-8.

1
JHS

Ce paramètre useOldUTF8Behavior = true a bien fonctionné pour moi. Il ne donnait pas d'erreur de chaîne incorrecte, mais convertissait des caractères spéciaux tels que à en plusieurs caractères et sauvegardés dans la base.

Pour éviter de telles situations, j'ai supprimé cette propriété du paramètre JDBC et converti le type de données de ma colonne en BLOB. Cela a fonctionné parfaitement. 

0
Prithu Kumar