java.sql.SQLException: ค่าสตริงไม่ถูกต้อง: '\ xF0 \ x9F \ x91 \ xBD \ xF0 \ x9F ... '


107

ฉันมีค่าสตริงต่อไปนี้: "walmart obama 👽💔"

ฉันใช้ MySQL และ Java

ฉันได้รับข้อยกเว้นต่อไปนี้: `` java.sql.SQLException: ค่าสตริงไม่ถูกต้อง: '\ xF0 \ x9F \ x91 \ xBD \ xF0 \ x9F ... '

นี่คือตัวแปรที่ฉันพยายามแทรกเข้าไป:

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

โค้ด Java ของฉันที่พยายามแทรก "walmart obama 👽💔" เป็นรหัสที่เตรียมไว้ ดังนั้นฉันจึงใช้setString()วิธี

ดูเหมือนว่าปัญหาคือการเข้ารหัสของค่า👽💔 ฉันจะแก้ไขปัญหานี้ได้อย่างไร? ก่อนหน้านี้ฉันใช้ Derby SQL และค่า👽💔เพิ่งจบลงด้วยการเป็นสอง sqaures (ฉันคิดว่านี่คือตัวแทนของอักขระ null)

ความช่วยเหลือทั้งหมดได้รับการชื่นชมอย่างมาก!


ดูเหมือนจะซ้ำกันของstackoverflow.com/questions/10957238/…
Joshua Davis

เมื่อคุณสร้างฐานข้อมูลคุณสามารถกำหนดชุดอักขระและการเรียงลำดับดังนี้CREATE DATABASE db_name CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Max Peng

คำตอบ:


145

สิ่งที่คุณมีคือEXTRATERRESTRIAL ALIEN (U+1F47D)และBROKEN HEART (U+1F494)สิ่งที่ไม่ได้อยู่ในเครื่องบินหลายภาษาพื้นฐาน พวกเขาไม่สามารถแสดงใน java เป็นอักขระเดียว"👽💔".length() == 4ได้ พวกเขาไม่ใช่อักขระว่างอย่างแน่นอนและจะเห็นช่องสี่เหลี่ยมหากคุณไม่ได้ใช้แบบอักษรที่รองรับ

MySQL utf8รองรับเฉพาะเครื่องบินหลายภาษาพื้นฐานและคุณต้องใช้utf8mb4แทน :

สำหรับอักขระเสริม utf8 ไม่สามารถจัดเก็บอักขระได้เลยในขณะที่ utf8mb4 ต้องการสี่ไบต์ในการจัดเก็บ เนื่องจาก utf8 ไม่สามารถจัดเก็บอักขระได้เลยคุณจึงไม่มีอักขระเสริมใด ๆ ในคอลัมน์ utf8 และคุณไม่จำเป็นต้องกังวลเกี่ยวกับการแปลงอักขระหรือการสูญเสียข้อมูลเมื่ออัปเกรดข้อมูล utf8 จาก MySQL เวอร์ชันเก่า

ดังนั้นเพื่อรองรับอักขระเหล่านี้ MySQL ของคุณต้องเป็น 5.5+ และคุณต้องใช้งานได้utf8mb4ทุกที่ การเชื่อมต่อการเข้ารหัสความต้องการที่จะutf8mb4ตั้งตัวจะต้องมีutf8mb4และ collaction utf8mb4ความต้องการที่จะเป็น สำหรับ java มันยังคงเป็นเพียง"utf-8"แต่ MySQL ต้องการความแตกต่าง

ฉันไม่รู้ว่าคุณใช้ไดรเวอร์อะไร แต่วิธีที่ไม่เชื่อเรื่องพระเจ้าของไดรเวอร์ในการตั้งค่าชุดอักขระการเชื่อมต่อคือการส่งแบบสอบถาม:

SET NAMES 'utf8mb4'

ทันทีหลังจากทำการเชื่อมต่อ

ดูสิ่งนี้สำหรับ Connector / J :

14.14: ฉันจะใช้ 4-byte UTF8, utf8mb4 กับ Connector / J ได้อย่างไร

ในการใช้ UTF8 4 ไบต์กับ Connector / J กำหนดค่าเซิร์ฟเวอร์ MySQL ด้วย character_set_server = utf8mb4 Connector / J แล้วจะใช้การตั้งค่าที่ ตราบเท่าที่ characterEncoding ยังไม่ได้รับการตั้งค่าในสตริงการเชื่อมต่อ สิ่งนี้เทียบเท่ากับการตรวจจับอัตโนมัติของชุดอักขระ

ปรับคอลัมน์และฐานข้อมูลของคุณด้วย:

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

อีกครั้งเวอร์ชัน MySQL ของคุณต้องค่อนข้างทันสมัยสำหรับการรองรับ utf8mb4


ตรวจสอบโพสต์อื่น ๆ ที่เกี่ยวข้องของฉัน: stackoverflow.com/questions/13748170/… . หากคุณสามารถตอบได้คุณก็จะตอบคำถามนี้เช่นกัน โพสต์อื่นมีรายละเอียดเพิ่มเติมเกี่ยวกับสิ่งที่ฉันได้ทำ
CodeKingPlusPlus

1
@CodeKingPlusPlus คุณได้เปลี่ยนทุกอย่างในฐานข้อมูลของคุณutf8mb4แล้วดูเหมือนว่าคุณยังใช้utf8_general_ci..
Esailija

1
อย่าทำ "SET NAMES" ด้วย 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
ในกรณีที่คุณต้องการกำจัดตัวละครจากภายนอก BMP แทนที่จะจัดการกับความยุ่งเหยิงในการเปลี่ยน DB ของคุณโปรดดูที่นี่: stackoverflow.com/questions/4035562/…
Indigenuity

2
ฉันมีปัญหาเดียวกันทำตามขั้นตอนข้างต้น แต่ไม่ได้รับการแก้ไขจนกระทั่งเปลี่ยน character-set-server = utf8mb4 ใน C: \ ProgramData \ MySQL \ MySQL Server 5.7 \ my.ini
fattah.safa

16

สรุปแล้วในการบันทึกสัญลักษณ์ที่ต้องใช้ 4 ไบต์คุณต้องอัปเดต characher-set และ collation สำหรับutf8mb4:

  1. ตารางฐานข้อมูล / คอลัมน์: alter table <some_table> convert to character set utf8mb4 collate utf8mb4_unicode_ci
  2. การเชื่อมต่อเซิร์ฟเวอร์ฐานข้อมูล ( ดู )

ในสภาพแวดล้อมการพัฒนาของฉันสำหรับ # 2 ฉันต้องการตั้งค่าพารามิเตอร์ในบรรทัดคำสั่งเมื่อเริ่มเซิร์ฟเวอร์: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci


btw ให้ความสนใจกับพฤติกรรม Connector / Jด้วยSET NAMES 'utf8mb4':

อย่าออกชื่อชุดการสืบค้นด้วย Connector / J เนื่องจากโปรแกรมควบคุมจะตรวจไม่พบว่าชุดอักขระมีการเปลี่ยนแปลงและจะยังคงใช้ชุดอักขระที่ตรวจพบในระหว่างการตั้งค่าการเชื่อมต่อครั้งแรก

และหลีกเลี่ยงการตั้งค่าcharacterEncodingพารามิเตอร์ใน url การเชื่อมต่อเนื่องจากจะแทนที่การเข้ารหัสเซิร์ฟเวอร์ที่กำหนดค่าไว้:

หากต้องการแทนที่การเข้ารหัสที่ตรวจพบโดยอัตโนมัติบนฝั่งไคลเอ็นต์ให้ใช้คุณสมบัติ characterEncoding ใน URL ที่ใช้เพื่อเชื่อมต่อกับเซิร์ฟเวอร์


15

น่าแปลกที่ฉันพบว่าการลบ&characterEncoding=UTF-8ออกจากJDBC urlเคล็ดลับนั้นทำให้ฉันมีปัญหาที่คล้ายกัน

จากคุณสมบัติของฉัน

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

ฉันคิดว่าสิ่งนี้สนับสนุนสิ่งที่ @Esailija กล่าวไว้ข้างต้นนั่นคือ MySQL ของฉันซึ่งเป็น 5.5 แน่นอนกำลังหารสชาติที่ชื่นชอบของการเข้ารหัส UTF-8

(หมายเหตุฉันยังระบุว่าInputStreamฉันกำลังอ่านจากUTF-8ในรหัส java ซึ่งอาจไม่เจ็บ) ...


อาจuseUnicode=trueจะไม่จำเป็นด้วยซ้ำ? ในกรณีของฉันสิ่งเดียวที่ใช้ได้คือการตั้งค่าcharacter_set_server=utf8mb4ทั่วโลกบนเซิร์ฟเวอร์ (กลุ่มพารามิเตอร์ RDS) และไม่มี characterEncoding ใด ๆ ใน JDBC URL
Joshua Davis

6

ฉันจะแก้ปัญหาของฉันได้อย่างไร

ฉันมี

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

ใน URL การเชื่อมต่อ jdbc hibernate ของฉันและฉันเปลี่ยนประเภทข้อมูลสตริงเป็นข้อความยาวในฐานข้อมูลซึ่งก่อนหน้านี้เป็น varchar


Greate ถ้าคุณไม่ต้องการให้คอลัมน์นั้นจัดทำดัชนีและมีขนาดค่อนข้างเล็ก แต่ฉันสามารถทำเคล็ดลับนี้กับคอลัมน์ทั้งหมดของฉันได้
shareef

3

ต่อท้ายบรรทัดuseUnicode=true&amp;characterEncoding=UTF-8ใน jdbc url ของคุณ

ในกรณีของคุณข้อมูลไม่ได้ถูกส่งโดยใช้การUTF-8เข้ารหัส


ฉันจะต่อท้ายสิ่งนี้ได้อย่างไร ในสายอักขระการเชื่อมต่อของฉัน? ฉันใช้ Netbeans ถ้าช่วยได้
CodeKingPlusPlus

คุณกำลังสร้างการเชื่อมต่ออย่างไร
JHS

DriverManager.getConnection ("jdbc: mysql: // localhost: #### / [dbName]", [ชื่อผู้ใช้], [รหัสผ่าน]);
CodeKingPlusPlus

ทำแบบนี้ - DriverManager.getConnection ("jdbc: mysql: // localhost: #### / [dbName]? useUnicode = true & amp; characterEncoding = UTF-8", [user name], [password]);
JHS

1
เกานั่นฉันลืม '?' แต่ตอนนี้ฉันกลับมาพบข้อผิดพลาดเหมือนเดิม ...
CodeKingPlusPlus

3

ฉันประสบปัญหาเดียวกันและแก้ไขได้โดยการตั้งค่าCollationเป็นutf8_general_ciสำหรับแต่ละคอลัมน์


2

ฉันเดาว่า MySQL ไม่เชื่อว่านี่เป็นข้อความ UTF8 ที่ถูกต้อง ฉันลองแทรกบนตารางทดสอบที่มีนิยามคอลัมน์เดียวกัน (การเชื่อมต่อไคลเอนต์ mysql ก็เป็น UTF8 เช่นกัน) และแม้ว่าจะทำการแทรกข้อมูลที่ฉันดึงมาด้วยไคลเอนต์ MySQL CLI และ JDBC ไม่ได้ดึงค่าอย่างถูกต้อง เพื่อให้แน่ใจว่า UTF8 ทำงานได้อย่างถูกต้องฉันจึงใส่ "ö" แทน "o" สำหรับโอบามา:

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

แอปพลิเคชัน java ขนาดเล็กที่จะทดสอบด้วย:

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

}

เอาท์พุต:

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

นอกจากนี้ฉันได้ลองใช้ส่วนแทรกเดียวกันกับการเชื่อมต่อ JDBC แล้วและมันก็มีข้อยกเว้นเดียวกันกับที่คุณได้รับ ฉันเชื่อว่านี่เป็นบั๊ก MySQL อาจจะมีรายงานจุดบกพร่องเกี่ยวกับสถานการณ์ดังกล่าวแล้ว ..


อย่างไรก็ตามอักขระในสตริงของคุณจะไม่แสดงอย่างถูกต้องทั้งใน Firefox และ Chrome บน OSX แสดงอย่างถูกต้องในแอปพลิเคชัน iTerm ของฉัน ฉันคิดว่านี่ขึ้นอยู่กับแบบอักษร
ศุกร์

1

ฉันมีปัญหาเดียวกันและหลังจากพิจารณาชุดอักขระทั้งหมดอย่างระมัดระวังและพบว่าพวกเขาถูกต้องฉันก็รู้ว่าคุณสมบัติที่ถูกบั๊กที่ฉันมีในชั้นเรียนมีคำอธิบายประกอบเป็น @Column แทนที่จะเป็น @JoinColumn (javax.presistence; hibernate) และ มันทำลายทุกอย่าง


1

ดำเนินการ

show VARIABLES like "%char%”;

ค้นหา character-set-server หากไม่ใช่ utf8mb4

ตั้งค่าใน my.cnf ของคุณเช่น

vim /etc/my.cnf

เพิ่มหนึ่งบรรทัด

character_set_server = utf8mb4

เมื่อรีสตาร์ท mysql ครั้งสุดท้าย


1
character_set_serverเป็นตัวเลือกไม่ใช่character-set-server
อรุณอาร์

0

การตั้งค่านี้ useOldUTF8Behavior = true ทำงานได้ดีสำหรับฉัน ไม่มีข้อผิดพลาดสตริงที่ไม่ถูกต้อง แต่จะแปลงอักขระพิเศษเช่นÃเป็นอักขระหลายตัวและบันทึกไว้ในฐานข้อมูล

เพื่อหลีกเลี่ยงสถานการณ์ดังกล่าวฉันลบคุณสมบัตินี้ออกจากพารามิเตอร์ JDBC และแปลงประเภทข้อมูลของคอลัมน์ของฉันเป็น BLOB แทน สิ่งนี้ทำงานได้อย่างสมบูรณ์แบบ


คุณช่วยเพิ่ม deatil เพิ่มเติมในคำตอบของคุณได้ไหม (code, commants ฯลฯ )
aBnormaLz

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.