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


107

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

J'utilise MySQL et Java.

J'obtiens 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 état préparé. J'utilise donc la setString()méthode.

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 👽💔 finissaient par être deux sqaures (je pense que c'est la représentation du caractère nul)

Toute aide est grandement appréciée!



Lorsque vous créez la base de données, vous pouvez donner le jeu de caractères et le classement comme ceci:CREATE DATABASE db_name CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Max Peng

Réponses:


145

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 en Java comme un char, "👽💔".length() == 4. Ce ne sont certainement pas des caractères nuls et on verra des carrés si vous n'utilisez pas de polices qui les prennent en charge.

MySQL utf8ne prend en charge que le plan multilingue de base, et vous devez utiliser à la utf8mb4place :

Pour un caractère supplémentaire, utf8 ne peut pas du tout stocker le caractère, tandis que utf8mb4 nécessite quatre octets pour le stocker. Comme utf8 ne peut pas du tout stocker le caractère, 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 de la perte de données lors de la mise à niveau des données utf8 à partir d'anciennes versions de MySQL.

Donc, pour prendre en charge ces caractères, votre MySQL doit être 5.5+ et vous devez l'utiliser utf8mb4partout. Le codage de connexion doit être utf8mb4, le jeu de caractères doit l'être utf8mb4et la collaction doit l'être utf8mb4. Pour java, c'est toujours juste "utf-8", mais MySQL a besoin d'une distinction.

Je ne sais pas quel pilote vous utilisez, mais un moyen indépendant de 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 puis-je utiliser UTF8 4 octets, utf8mb4 avec Connector / J?

Pour utiliser UTF8 4 octets avec Connector / J, configurez le serveur MySQL avec character_set_server = utf8mb4. Connector / J utilisera alors ce paramètre tant que characterEncoding n'a pas été défini dans la chaîne de connexion . 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 le support utf8mb4.


Consultez mon autre article connexe: stackoverflow.com/questions/13748170/… . Si vous pouvez y répondre, vous aurez également répondu à cette question. L'autre poste contient plus de détails sur ce que j'ai fait.
CodeKingPlusPlus

1
@CodeKingPlusPlus avez-vous tout changé dans votre base de données en utf8mb4, il semble que vous utilisez toujours utf8_general_ci..
Esailija

1
Ne faites pas "SET NAMES" avec 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
Si vous souhaitez simplement vous débarrasser des personnages extérieurs au BMP au lieu de vous occuper du désordre de changer votre base de données, voir ici: stackoverflow.com/questions/4035562/…
Indigenuity

2
J'ai le même problème, j'ai suivi les étapes ci-dessus mais je n'ai pas été résolu avant de changer le jeu de caractères-server = utf8mb4 dans C: \ ProgramData \ MySQL \ MySQL Server 5.7 \ my.ini
fattah.safa

16

Dans l'ensemble, pour enregistrer les symboles qui nécessitent 4 octets, vous devez mettre à jour le jeu de caractères et le classement pour utf8mb4:

  1. table / colonne de 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 sur la ligne de commande lors du démarrage du serveur: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci


btw, faites attention au comportement de Connector / 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 changé et continuera à utiliser le jeu de caractères détecté lors de la configuration initiale de la connexion.

Et évitez de définir le characterEncodingparamètre dans l'URL de connexion car il remplacera le codage du serveur configuré:

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


15

Bizarrement, j'ai trouvé que SUPPRIMER &characterEncoding=UTF-8du a JDBC urlfait l'affaire pour moi avec des problèmes similaires.

Sur la base de mes propriétés,

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

Je pense que cela confirme ce que @Esailija a dit ci-dessus, c'est-à-dire que MySQL, qui est en fait 5.5, est en train de découvrir sa propre saveur préférée de l'encodage UTF-8.

(Remarque, je spécifie également le InputStreamque je lis comme UTF-8dans le code java, ce qui ne fait probablement pas de mal) ...


Peut useUnicode=true-être même pas nécessaire? Dans mon cas, la seule chose qui a fonctionné est de définir character_set_server=utf8mb4globalement sur le serveur (groupe de paramètres RDS) et de ne PAS avoir de codage de caractère dans l'URL JDBC.
Joshua Davis

6

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 de chaîne en longtext dans la base de données, qui était auparavant varchar.


Génial si vous n'avez pas besoin que cette colonne soit indexée et qu'elle soit relativement petite, mais je peux faire cette astuce pour toutes mes colonnes
shareef

3

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

Dans votre cas, les données ne sont pas envoyées par UTF-8encodage.


Comment ajouter ceci? Dans ma chaîne de connexion? J'utilise Netbeans si cela aide.
CodeKingPlusPlus

Comment créez-vous la connexion?
JHS

DriverManager.getConnection ("jdbc: mysql: // localhost: #### / [dbName]", [nom d'utilisateur], [mot de passe]);
CodeKingPlusPlus

Faites-le comme ceci - DriverManager.getConnection ("jdbc: mysql: // localhost: #### / [dbName]? UseUnicode = true & amp; characterEncoding = UTF-8", [nom d'utilisateur], [mot de passe]);
JHS

1
Grattez ça, j'ai oublié le '?' Mais maintenant, je suis de retour à la même erreur que le message d'origine ...
CodeKingPlusPlus

3

Je fait face à la même question et résolu en définissant la Collation à utf8_general_ci pour chaque colonne.


2

Je suppose que MySQL ne pense pas que ce soit un 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 qu'elle ait fait l'insertion, les données que j'ai récupérées avec le client MySQL CLI ainsi que JDBC n'ont pas récupéré les valeurs correctement. Pour être sûr que UTF8 fonctionnait correctement, j'ai inséré un "ö" au lieu 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();
        }
    }

}

Production:

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

De plus, j'ai essayé le même insert avec la connexion JDBC et il a jeté la même exception que vous obtenez. Je pense que c'est un bogue MySQL. Il y a peut-être déjà un rapport de bogue concernant une telle situation.


À propos, les caractères de votre chaîne ne s'affichent même pas correctement dans Firefox et Chrome sous OSX. Ils s'affichent correctement dans mon application iTerm. Je pense que cela dépend de la police.
vendredi

1

J'ai eu le même problème et après avoir soigneusement examiné tous les jeux de caractères et constaté qu'ils allaient bien, j'ai réalisé que la propriété buguée que j'avais dans ma classe était annotée comme @Column au lieu de @JoinColumn (javax.presistence; hibernate) et il cassait tout.


1

exécuter

show VARIABLES like "%char%”;

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

placez-le dans votre my.cnf, comme

vim /etc/my.cnf

ajouter une ligne

character_set_server = utf8mb4

au dernier redémarrage mysql


1
character_set_serverest l'option, PAScharacter-set-server
Arun SR

0

Ce paramètre useOldUTF8Behavior = true a bien fonctionné pour moi. Il n'a donné aucune erreur de chaîne incorrecte, mais il a converti des caractères spéciaux tels que à en plusieurs caractères et enregistré dans la base de données.

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


Pourriez-vous s'il vous plaît ajouter plus de détails à votre réponse? (code, commants, etc.)
aBnormaLz

-2

En outre, le type de données peut utiliser l'installation blob de varchar ou de texte.


Vous ne voulez pas ça
ECostello
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.