java.sql.SQLException: Niepoprawna wartość ciągu: „\ xF0 \ x9F \ x91 \ xBD \ xF0 \ x9F…”


107

Mam następującą wartość ciągu: „walmart obama 👽💔”

Używam MySQL i Java.

Otrzymuję następujący wyjątek: `java.sql.SQLException: Niepoprawna wartość ciągu: '\ xF0 \ x9F \ x91 \ xBD \ xF0 \ x9F ...'

Oto zmienna, do której próbuję wstawić:

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

Mój kod Java, który próbuje wstawić „walmart obama 👽💔”, jest przygotowanym oświadczeniem. Więc używam setString()metody.

Wygląda na to, że problemem jest kodowanie wartości 👽💔. Jak mogę to naprawić? Wcześniej używałem Derby SQL, a wartości 👽💔 skończyły się na dwóch kwadratach kwadratowych (myślę, że jest to reprezentacja znaku null)

Każda pomoc jest mile widziana!


Wygląda na to, że jest to duplikat stackoverflow.com/questions/10957238/ ...
Joshua Davis

Podczas tworzenia bazy danych możesz podać zestaw znaków i sortowanie w następujący sposób:CREATE DATABASE db_name CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Max Peng

Odpowiedzi:


145

To, co masz, jest, EXTRATERRESTRIAL ALIEN (U+1F47D)a BROKEN HEART (U+1F494)czego nie ma na podstawowym planie wielojęzyczności. Nie mogą być one nawet w Javie reprezentowana jako jeden char "👽💔".length() == 4. Zdecydowanie nie są to znaki puste i jeśli nie używasz czcionek, które je obsługują, zobaczysz kwadraty.

MySQL utf8obsługuje tylko podstawową płaszczyznę wielojęzyczną, a utf8mb4zamiast tego musisz użyć :

W przypadku znaku dodatkowego utf8 nie może w ogóle przechowywać znaku, podczas gdy utf8mb4 wymaga czterech bajtów do jego przechowywania. Ponieważ utf8 w ogóle nie może przechowywać znaku, nie masz żadnych dodatkowych znaków w kolumnach utf8 i nie musisz martwić się o konwersję znaków lub utratę danych podczas aktualizacji danych utf8 ze starszych wersji MySQL.

Aby obsługiwać te znaki, Twój MySQL musi mieć wersję 5.5+ i musisz go używać utf8mb4wszędzie. Konieczne jest kodowanie połączenia utf8mb4, zestaw znaków utf8mb4i kolakcja utf8mb4. W przypadku javy to nadal tylko "utf-8"kwestia, ale MySQL wymaga rozróżnienia.

Nie wiem, jakiego sterownika używasz, ale niezależnym od sterownika sposobem ustawienia zestawu znaków połączenia jest wysłanie zapytania:

SET NAMES 'utf8mb4'

Zaraz po nawiązaniu połączenia.

Zobacz także to dla złącza / J :

14.14: Jak mogę używać 4-bajtowego UTF8, utf8mb4 ze złączem / J?

Aby używać 4-bajtowego UTF8 z łącznikiem / J, skonfiguruj serwer MySQL za pomocą character_set_server = utf8mb4. Connector / J użyje tego ustawienia, o ile characterEncoding nie zostanie ustawiona w parametrach połączenia . Jest to równoważne z automatycznym wykrywaniem zestawu znaków.

Dostosuj również kolumny i bazę danych:

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

Twoja wersja MySQL musi być stosunkowo aktualna, aby obsługiwać utf8mb4.


Sprawdź mój inny powiązany post: stackoverflow.com/questions/13748170/… . Jeśli potrafisz na nie odpowiedzieć, odpowiesz również na to pytanie. Drugi post zawiera więcej szczegółów na temat tego, co zrobiłem.
CodeKingPlusPlus,

1
@CodeKingPlusPlus zmieniłeś wszystko w swojej bazie danych na utf8mb4, wygląda na to, że nadal używasz utf8_general_ci
Esailija,

1
Nie używaj 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.
opcji

1
Jeśli chcesz po prostu pozbyć się postaci spoza BMP, zamiast zajmować się bałaganem związanym ze zmianą bazy danych, zobacz tutaj: stackoverflow.com/questions/4035562/…
Indigenuity

2
Mam ten sam problem, wykonałem powyższe kroki, ale nie został rozwiązany, dopóki nie zmieniłem zestawu znaków-set-server = utf8mb4 w C: \ ProgramData \ MySQL \ MySQL Server 5.7 \ my.ini
fattah.safa

16

Podsumowując, aby zapisać symbole wymagające 4 bajtów, należy zaktualizować zestaw znaków i sortowanie dla utf8mb4:

  1. tabela / kolumna bazy danych: alter table <some_table> convert to character set utf8mb4 collate utf8mb4_unicode_ci
  2. połączenie z serwerem bazy danych ( zobacz )

W moim środowisku programistycznym dla # 2 wolę ustawiać parametry w wierszu poleceń podczas uruchamiania serwera: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci


btw, zwróć uwagę na zachowanie złącza / J z SET NAMES 'utf8mb4':

Nie wysyłaj nazw zestawów zapytań za pomocą Connector / J, ponieważ sterownik nie wykryje zmiany zestawu znaków i będzie nadal używać zestawu znaków wykrytego podczas początkowej konfiguracji połączenia.

I unikaj ustawiania characterEncodingparametru w adresie URL połączenia, ponieważ zastąpi to skonfigurowane kodowanie serwera:

Aby przesłonić automatycznie wykryte kodowanie po stronie klienta, użyj właściwości characterEncoding w adresie URL używanym do łączenia się z serwerem.


15

Co dziwne, odkryłem, że USUWANIE &characterEncoding=UTF-8zJDBC url z podobnymi problemami.

Na podstawie moich właściwości

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

Myślę, że to potwierdza to, co powiedział @Esailija powyżej, tj. Mój MySQL, który jest rzeczywiście 5.5, odkrywa swój własny ulubiony smak kodowania UTF-8.

(Uwaga, określam również, z InputStreamktórego czytam, jak UTF-8w kodzie java, co prawdopodobnie nie boli) ...


Może useUnicode=truenawet nie jest potrzebny? W moim przypadku jedyne, co zadziałało, to ustawienie character_set_server=utf8mb4globalne na serwerze (grupa parametrów RDS) i NIE posiadanie żadnego kodowania znaków w adresie URL JDBC.
Joshua Davis,

6

Jak rozwiązałem swój problem.

miałem

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

W moim hibernacyjnym adresie URL połączenia jdbc i zmieniłem typ danych ciągu na longtext w bazie danych, który wcześniej był varchar.


Witam, jeśli nie potrzebujesz indeksowania tej kolumny i jest ona stosunkowo mała, ale mogę zrobić tę sztuczkę dla wszystkich moich kolumn
shareef,

3

Dołącz linię useUnicode=true&amp;characterEncoding=UTF-8 do adresu URL jdbc.

W Twoim przypadku dane nie są przesyłane za pomocą UTF-8kodowania.


Jak to dołączyć? W moim ciągu połączenia? Używam Netbeans, jeśli to pomaga.
CodeKingPlusPlus

Jak tworzysz połączenie?
JHS

DriverManager.getConnection ("jdbc: mysql: // localhost: #### / [nazwa_db]", [nazwa użytkownika], [hasło]);
CodeKingPlusPlus

Zrób to w ten sposób - DriverManager.getConnection ("jdbc: mysql: // localhost: #### / [nazwa_db]? UseUnicode = true & amp; characterEncoding = UTF-8", [nazwa użytkownika], [hasło]);
JHS

1
Podrap to, zapomniałem znaku „?” Ale teraz wracam do tego samego błędu, co w oryginalnym poście ...
CodeKingPlusPlus

3

Napotkałem ten sam problem i rozwiązałem go, ustawiając Collation na utf8_general_ci dla każdej kolumny.


2

Myślę, że MySQL nie uważa tego za poprawny tekst UTF8. Wypróbowałem wstawianie na tabeli testowej z tą samą definicją kolumny (połączenie klienta mysql było również UTF8) i chociaż wstawiło to, dane pobrane przez klienta MySQL CLI, a także JDBC nie pobrały poprawnie wartości. Aby upewnić się, że UTF8 działa poprawnie, wstawiłem „ö” zamiast „o” dla obamy:

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

Mała aplikacja Java do przetestowania z:

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

}

Wynik:

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

Ponadto wypróbowałem tę samą wkładkę z połączeniem JDBC i wyrzuciła ten sam wyjątek, który otrzymujesz. Uważam, że to błąd MySQL. Może jest już raport o błędzie dotyczący takiej sytuacji ..


Nawiasem mówiąc, znaki w twoim ciągu nie pojawiają się nawet poprawnie w Firefoksie i Chrome na OSX. Pojawiają się poprawnie w mojej aplikacji iTerm. Myślę, że to zależy od czcionki.
Friek

1

Miałem podobny problem i po uważnym sprawdzeniu wszystkich zestawów znaków i stwierdzeniu, że są w porządku, zdałem sobie sprawę, że błędna właściwość, którą miałem w mojej klasie, została oznaczona jako @Column zamiast @JoinColumn (javax.presistence; hibernate) i to wszystko zrywało.


1

wykonać

show VARIABLES like "%char%”;

znajdź serwer zestawu znaków, jeśli nie jest to utf8mb4.

ustaw go w swoim my.cnf, na przykład

vim /etc/my.cnf

dodaj jedną linię

character_set_server = utf8mb4

w końcu uruchom ponownie mysql


1
character_set_serverjest opcją, NIEcharacter-set-server
Arun SR,

0

To ustawienie useOldUTF8Behavior = true działało dobrze dla mnie. Nie dawał nieprawidłowych błędów w łańcuchach, ale konwertował znaki specjalne, takie jak Ã, na wiele znaków i zapisywał w bazie danych.

Aby uniknąć takich sytuacji, usunąłem tę właściwość z parametru JDBC i zamiast tego przekonwertowałem typ danych mojej kolumny na BLOB. To działało idealnie.


Czy mógłbyś dodać więcej szczegółów do swojej odpowiedzi? (kod, komanty itp.)
aBnormaLz

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.