java.sql.SQLException: Nilai string salah: '\ xF0 \ x9F \ x91 \ xBD \ xF0 \ x9F…'


107

Saya memiliki nilai string berikut: "walmart obama πŸ‘½πŸ’”"

Saya menggunakan MySQL dan Java.

Saya mendapatkan pengecualian berikut: `java.sql.SQLException: Nilai string salah: '\ xF0 \ x9F \ x91 \ xBD \ xF0 \ x9F ...'

Berikut adalah variabel yang saya coba masukkan:

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

Kode Java saya yang mencoba memasukkan "walmart obama πŸ‘½πŸ’”" adalah preparedStatement. Jadi saya menggunakan setString()metode ini.

Sepertinya masalahnya adalah pengkodean nilai πŸ‘½πŸ’”. Bagaimana cara memperbaikinya? Sebelumnya saya menggunakan Derby SQL dan nilainya πŸ‘½πŸ’” baru saja menjadi dua sqaures (saya pikir ini adalah representasi dari karakter nol)

Semua bantuan sangat dihargai!



Saat Anda membuat database, Anda dapat memberikan set karakter dan susunan seperti ini:CREATE DATABASE db_name CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Max Peng

Jawaban:


145

Apa yang Anda miliki EXTRATERRESTRIAL ALIEN (U+1F47D)dan BROKEN HEART (U+1F494)yang tidak berada dalam bidang multibahasa dasar. Mereka bahkan tidak dapat direpresentasikan di java sebagai satu karakter "πŸ‘½πŸ’”".length() == 4,. Mereka jelas bukan karakter nol dan orang akan melihat kotak jika Anda tidak menggunakan font yang mendukungnya.

MySQL utf8hanya mendukung pesawat dasar multibahasa, dan Anda perlu menggunakan utf8mb4gantinya :

Untuk karakter tambahan, utf8 tidak dapat menyimpan karakter sama sekali, sedangkan utf8mb4 membutuhkan empat byte untuk menyimpannya. Karena utf8 tidak dapat menyimpan karakter sama sekali, Anda tidak memiliki karakter tambahan di kolom utf8 dan Anda tidak perlu khawatir tentang mengonversi karakter atau kehilangan data saat memutakhirkan data utf8 dari versi MySQL yang lebih lama.

Jadi untuk mendukung karakter ini, MySQL Anda harus 5,5+ dan Anda harus menggunakannya di utf8mb4mana saja. Pengkodean koneksi perlu dilakukan utf8mb4, kumpulan karakter perlu utf8mb4dan pengumpulan harus dilakukan utf8mb4. Untuk java masih saja"utf-8" , tapi MySQL membutuhkan perbedaan.

Saya tidak tahu driver apa yang Anda gunakan tetapi cara driver agnostik untuk mengatur charset koneksi adalah dengan mengirim kueri:

SET NAMES 'utf8mb4'

Tepat setelah membuat koneksi.

Lihat juga ini untuk Connector / J :

14.14: Bagaimana cara menggunakan UTF8 4-byte, utf8mb4 dengan Connector / J?

Untuk menggunakan UTF8 4-byte dengan Connector / J mengkonfigurasi server MySQL dengan character_set_server = utf8mb4. Connector / J kemudian akan menggunakan pengaturan itu selama characterEncoding belum diatur dalam string koneksi . Ini sama dengan deteksi otomatis himpunan karakter.

Sesuaikan juga kolom dan database Anda:

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

Sekali lagi, versi MySQL Anda harus relatif mutakhir untuk dukungan utf8mb4.


Lihat posting terkait saya yang lain: stackoverflow.com/questions/13748170/… . Jika Anda bisa menjawabnya, maka Anda pasti sudah menjawab pertanyaan ini juga. Posting lainnya memiliki detail lebih lanjut tentang apa yang telah saya lakukan.
CodeKingPlusPlus

1
@CodeKingPlusPlus sudahkah Anda mengubah semua yang ada di database Anda utf8mb4, sepertinya Anda masih menggunakan utf8_general_ci..
Esailija

1
Jangan lakukan "SET NAMA" dengan 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
Jika Anda ingin menyingkirkan karakter dari luar BMP daripada berurusan dengan kekacauan mengubah DB Anda, lihat di sini: stackoverflow.com/questions/4035562/…
Indigenuity

2
Saya memiliki masalah yang sama, mengikuti langkah-langkah di atas tetapi tidak terselesaikan sampai mengubah karakter-set-server = utf8mb4 di C: \ ProgramData \ MySQL \ MySQL Server 5.7 \ my.ini
fattah.safa

16

Secara keseluruhan, untuk menyimpan simbol yang membutuhkan 4 byte, Anda perlu memperbarui kumpulan karakter dan pemeriksaan utf8mb4 :

  1. tabel / kolom database: alter table <some_table> convert to character set utf8mb4 collate utf8mb4_unicode_ci
  2. koneksi server database ( lihat )

Di lingkungan pengembangan saya untuk # 2, saya lebih suka mengatur parameter pada baris perintah saat memulai server: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci


btw, perhatikan perilaku Connector / J dengan SET NAMES 'utf8mb4':

Jangan keluarkan nama kumpulan kueri dengan Connector / J, karena driver tidak akan mendeteksi bahwa kumpulan karakter telah berubah, dan akan terus menggunakan kumpulan karakter yang terdeteksi selama penyiapan koneksi awal.

Dan hindari mengatur characterEncodingparameter di url koneksi karena akan menggantikan encoding server yang dikonfigurasi:

Untuk menimpa pengkodean yang terdeteksi secara otomatis di sisi klien, gunakan properti characterEncoding di URL yang digunakan untuk menyambung ke server.


15

Anehnya, saya menemukan bahwa MENGHAPUS &characterEncoding=UTF-8dariJDBC url melakukan trik bagi saya dengan masalah serupa.

Berdasarkan properti saya,

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

Saya rasa ini mendukung apa yang dikatakan @Esailija di atas, yaitu MySQL saya, yang memang 5,5, sedang mencari tahu citarasa favoritnya sendiri dari pengkodean UTF-8.

(Catatan, saya juga menentukan InputStreamI'm reading from seperti UTF-8pada kode java, yang mungkin tidak sakit) ...


Mungkin useUnicode=truebahkan tidak dibutuhkan? Dalam kasus saya, satu-satunya yang berhasil adalah pengaturan character_set_server=utf8mb4global di server (grup parameter RDS) dan TIDAK memiliki characterEncoding di URL JDBC.
Joshua Davis

6

Bagaimana saya memecahkan masalah saya.

Saya punya

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

Di url koneksi jdbc hibernate saya dan saya mengubah tipe data string menjadi teks panjang dalam database, yang sebelumnya varchar.


Bagus jika Anda tidak membutuhkan kolom itu diindeks dan relatif kecil, tetapi saya dapat melakukan trik ini untuk semua kolom saya
shareef

3

Tambahkan baris useUnicode=true&amp;characterEncoding=UTF-8 ke url jdbc Anda.

Dalam kasus Anda, data tidak sedang dikirim menggunakan UTF-8 pengkodean.


Bagaimana cara menambahkan ini? Dalam string koneksi saya? Saya menggunakan Netbeans jika itu membantu.
CodeKingPlusPlus

Bagaimana Anda membuat koneksi?
JHS

DriverManager.getConnection ("jdbc: mysql: // localhost: #### / [dbName]", [nama pengguna], [sandi]);
CodeKingPlusPlus

Lakukan seperti ini - DriverManager.getConnection ("jdbc: mysql: // localhost: #### / [dbName]? UseUnicode = true & amp; characterEncoding = UTF-8", [nama pengguna], [sandi]);
JHS

1
Gores itu, saya lupa '?' Tapi sekarang saya kembali ke kesalahan yang sama seperti posting asli ...
CodeKingPlusPlus

3

Saya menghadapi masalah yang sama dan menyelesaikannya dengan mengatur Collation ke utf8_general_ci untuk setiap kolom.


2

Saya kira MySQL tidak percaya ini menjadi teks UTF8 yang valid. Saya mencoba menyisipkan pada tabel uji dengan definisi kolom yang sama (koneksi klien mysql juga UTF8) dan meskipun melakukan penyisipan, data yang saya ambil dengan klien MySQL CLI serta JDBC tidak mengambil nilai dengan benar. Untuk memastikan UTF8 berfungsi dengan benar, saya memasukkan "ΓΆ" alih-alih "o" untuk 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

Aplikasi java kecil untuk diuji dengan:

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

}

Keluaran:

johan@appel:~/workspaces/java/javatest/bin$ java test.sql.Test
test string=walmart ΓΆbama πŸ‘½πŸ’”
retrieved="walmart ΓΆbama "

Juga, saya telah mencoba sisipan yang sama dengan koneksi JDBC dan itu memberikan pengecualian yang sama seperti yang Anda dapatkan. Saya yakin ini adalah bug MySQL. Mungkin sudah ada laporan bug tentang situasi seperti itu ..


Ngomong-ngomong, karakter dalam string Anda bahkan tidak muncul dengan benar di Firefox dan Chrome di OSX. Mereka muncul dengan benar di aplikasi iTerm saya. Saya pikir ini tergantung font.
Jum

1

Saya memiliki masalah yang sama dan setelah berhati-hati terhadap semua rangkaian karakter dan menemukan bahwa semuanya baik-baik saja, saya menyadari bahwa properti yang disadap yang saya miliki di kelas saya diberi anotasi sebagai @Column alih-alih @JoinColumn (javax.presistence; hibernate) dan itu menghancurkan segalanya.


1

menjalankan

show VARIABLES like "%char%”;

temukan character-set-server jika bukan utf8mb4.

setel di my.cnf Anda, seperti

vim /etc/my.cnf

tambahkan satu baris

character_set_server = utf8mb4

akhirnya restart mysql


1
character_set_serveradalah opsinya, TIDAKcharacter-set-server
Arun SR

0

Pengaturan ini useOldUTF8Behavior = true berfungsi dengan baik untuk saya. Itu tidak memberikan kesalahan string yang salah tetapi itu mengubah karakter khusus seperti Γƒ menjadi beberapa karakter dan disimpan dalam database.

Untuk menghindari situasi seperti itu, saya menghapus properti ini dari parameter JDBC dan alih-alih mengubah tipe data kolom saya ke BLOB. Ini bekerja dengan sempurna.


Bisakah Anda menambahkan lebih banyak deatil ke jawaban Anda? (kode, commants, etc.)
aBnormaLz

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.