java.sql.SQLException: Giá trị chuỗi không chính xác: '\ xF0 \ x9F \ x91 \ xBD \ xF0 \ x9F…'


107

Tôi có giá trị chuỗi sau: "walmart obama 👽💔"

Tôi đang sử dụng MySQL và Java.

Tôi nhận được ngoại lệ sau: `java.sql.SQLException: Giá trị chuỗi không chính xác: '\ xF0 \ x9F \ x91 \ xBD \ xF0 \ x9F ...'

Đây là biến tôi đang cố gắng chèn vào:

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

Mã Java của tôi đang cố chèn "walmart obama 👽💔" là một Chuẩn bị sẵn sàng. Vì vậy, tôi đang sử dụng setString()phương pháp.

Có vẻ như vấn đề là mã hóa các giá trị 👽💔. Làm thế nào tôi có thể sửa lỗi này? Trước đây tôi đã sử dụng Derby SQL và các giá trị 👽💔 chỉ kết thúc là hai sqaures (tôi nghĩ đây là đại diện của ký tự null)

Tất cả sự giúp đỡ được đánh giá rất cao!


Có vẻ như một bản sao của stackoverflow.com/questions/10957238/…
Joshua Davis

Khi bạn tạo cơ sở dữ liệu, bạn có thể đặt ký tự và đối chiếu như thế này:CREATE DATABASE db_name CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Max Peng

Câu trả lời:


145

Những gì bạn có là gì EXTRATERRESTRIAL ALIEN (U+1F47D)BROKEN HEART (U+1F494)không có trong bình diện đa ngôn ngữ cơ bản. Chúng thậm chí không thể được biểu diễn trong java dưới dạng một ký tự "👽💔".length() == 4,. Chúng chắc chắn không phải là các ký tự rỗng và người ta sẽ thấy các ô vuông nếu bạn không sử dụng các phông chữ hỗ trợ chúng.

MySQL utf8chỉ hỗ trợ mặt phẳng đa ngôn ngữ cơ bản và utf8mb4thay vào đó bạn cần sử dụng :

Đối với một ký tự bổ sung, utf8 hoàn toàn không thể lưu trữ ký tự, trong khi utf8mb4 yêu cầu bốn byte để lưu trữ. Vì utf8 hoàn toàn không thể lưu trữ ký tự, bạn không có bất kỳ ký tự bổ sung nào trong các cột utf8 và bạn không cần lo lắng về việc chuyển đổi ký tự hoặc mất dữ liệu khi nâng cấp dữ liệu utf8 từ các phiên bản MySQL cũ hơn.

Vì vậy, để hỗ trợ các ký tự này, MySQL của bạn cần phải là 5.5+ và bạn cần sử dụng utf8mb4ở mọi nơi. Cần phải có mã hóa kết nối, cần phải có utf8mb4bộ ký tự utf8mb4và cần phải có cấu kết utf8mb4. Đối với java, nó vẫn chỉ là một "utf-8", nhưng MySQL cần một sự khác biệt.

Tôi không biết bạn đang sử dụng trình điều khiển nào nhưng một cách bất khả tri dành cho trình điều khiển để đặt bộ mã kết nối là gửi truy vấn:

SET NAMES 'utf8mb4'

Ngay sau khi thực hiện kết nối.

Xem thêm điều này cho Connector / J :

14.14: Làm cách nào để sử dụng UTF8 4 byte, utf8mb4 với Connector / J?

Để sử dụng UTF8 4 byte với Connector / J, hãy cấu hình máy chủ MySQL với character_set_server = utf8mb4. Connector / J sau đó sẽ sử dụng cài đặt đó miễn là chưa đặt Mã hóa ký tự trong chuỗi kết nối . Điều này tương đương với tự động phát hiện bộ ký tự.

Điều chỉnh các cột và cơ sở dữ liệu của bạn:

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

Một lần nữa, phiên bản MySQL của bạn cần phải tương đối cập nhật để hỗ trợ utf8mb4.


Kiểm tra bài đăng liên quan khác của tôi: stackoverflow.com/questions/13748170/… . Nếu bạn có thể trả lời nó, thì bạn cũng sẽ trả lời được câu hỏi này. Bài đăng khác có thêm chi tiết về những gì tôi đã làm.
CodeKingPlusPlus

1
@CodeKingPlusPlus bạn đã thay đổi mọi thứ trong cơ sở dữ liệu của mình thành chưa utf8mb4, có vẻ như bạn vẫn đang sử dụng utf8_general_ci..
Esailija 7/12/12

1
Không thực hiện "ĐẶT TÊN" bằng Trình kết nối / 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
Trong trường hợp bạn muốn chỉ thoát khỏi các nhân vật từ bên ngoài BMP thay vì đối phó với những mớ hỗn độn của việc thay đổi DB của bạn, xem tại đây: stackoverflow.com/questions/4035562/...
Indigenuity

2
Tôi có cùng một vấn đề, theo các bước trên nhưng không được giải quyết cho đến khi thay đổi nhân vật-set-server = utf8mb4 trong C: \ ProgramData \ MySQL \ MySQL Server 5.7 \ my.ini
fattah.safa

16

Nói chung, để lưu các ký hiệu yêu cầu 4 byte, bạn cần cập nhật bộ characher và đối chiếu cho utf8mb4:

  1. bảng / cột cơ sở dữ liệu: alter table <some_table> convert to character set utf8mb4 collate utf8mb4_unicode_ci
  2. kết nối máy chủ cơ sở dữ liệu ( xem )

Trên enviromnt phát triển của tôi cho # 2, tôi thích đặt các tham số trên dòng lệnh khi khởi động máy chủ: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci


btw, hãy chú ý đến hành vi Connector / J với SET NAMES 'utf8mb4':

Không đặt tên bộ truy vấn với Trình kết nối / J, vì trình điều khiển sẽ không phát hiện ra rằng bộ ký tự đã thay đổi và sẽ tiếp tục sử dụng bộ ký tự được phát hiện trong quá trình thiết lập kết nối ban đầu.

Và tránh đặt characterEncodingtham số trong url kết nối vì nó sẽ ghi đè mã hóa máy chủ đã định cấu hình:

Để ghi đè mã hóa được phát hiện tự động ở phía máy khách, hãy sử dụng thuộc tính characterEncoding trong URL được sử dụng để kết nối với máy chủ.


15

Thật kỳ lạ, tôi thấy rằng việc LOẠI BỎ &characterEncoding=UTF-8khỏi điều JDBC urlđã xảy ra với tôi với các vấn đề tương tự.

Dựa trên tài sản của tôi,

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

Tôi nghĩ rằng điều này hỗ trợ những gì @Esailija đã nói ở trên, tức là MySQL của tôi, thực sự là 5.5, đang tìm ra hương vị yêu thích của mã hóa UTF-8.

(Lưu ý, tôi cũng chỉ định InputStreamtôi đang đọc từ UTF-8trong mã java, điều này có thể không ảnh hưởng gì) ...


Có lẽ useUnicode=truethậm chí không cần thiết? Trong trường hợp của tôi, điều duy nhất hoạt động là thiết lập character_set_server=utf8mb4toàn cục trên máy chủ (nhóm tham số RDS) và KHÔNG có bất kỳ Mã hóa ký tự nào trong URL JDBC.
Joshua Davis

6

Tôi đã giải quyết vấn đề của mình như thế nào.

Tôi đã có

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

Trong url kết nối jdbc ngủ đông của tôi và tôi đã thay đổi kiểu dữ liệu chuỗi thành văn bản dài trong cơ sở dữ liệu, trước đây là varchar.


Greate nếu bạn không cần cột lập chỉ mục và nó tương đối nhỏ, nhưng tôi có thể làm thủ thuật này cho tất cả các cột của tôi mặc dù
Shareef

3

Nối dòng useUnicode=true&amp;characterEncoding=UTF-8vào url jdbc của bạn.

Trong trường hợp của bạn, dữ liệu không được gửi bằng UTF-8mã hóa.


Làm cách nào để nối cái này? Trong chuỗi kết nối của tôi? Tôi đang sử dụng Netbeans nếu điều đó có ích.
CodeKingPlusPlus

Bạn đang tạo kết nối như thế nào?
JHS

DriverManager.getConnection ("jdbc: mysql: // localhost: #### / [dbName]", [tên người dùng], [mật khẩu]);
CodeKingPlusPlus

Làm như thế này - DriverManager.getConnection ("jdbc: mysql: // localhost: #### / [dbName]? UseUnicode = true & amp; characterEncoding = UTF-8", [user name], [password]);
JHS

1
Cào đi, tôi quên dấu '?' Nhưng bây giờ tôi trở lại những lỗi tương tự như các bài bản gốc ...
CodeKingPlusPlus

3

Tôi gặp phải vấn đề tương tự và đã giải quyết nó bằng cách đặt Đối chiếu thành utf8_general_ci cho mỗi cột.


2

Tôi đoán MySQL không tin rằng đây là văn bản UTF8 hợp lệ. Tôi đã thử chèn trên bảng thử nghiệm có cùng định nghĩa cột (kết nối máy khách mysql cũng là UTF8) và mặc dù nó đã thực hiện chèn, dữ liệu tôi truy xuất bằng máy khách MySQL CLI cũng như JDBC không truy xuất các giá trị một cách chính xác. Để đảm bảo UTF8 hoạt động chính xác, tôi đã chèn "ö" thay vì "o" cho 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

Ứng dụng java nhỏ để kiểm tra với:

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

}

Đầu ra:

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

Ngoài ra, tôi đã thử chèn tương tự với kết nối JDBC và nó ném ra cùng một ngoại lệ mà bạn đang nhận được. Tôi tin rằng đây là một lỗi MySQL. Có lẽ đã có một báo cáo lỗi về tình huống như vậy rồi ..


Nhân tiện, các ký tự trong chuỗi của bạn thậm chí không hiển thị chính xác trong cả Firefox và Chrome trên OSX. Chúng hiển thị chính xác trong ứng dụng iTerm của tôi. Tôi nghĩ rằng điều này là phụ thuộc vào phông chữ.
Friek

1

Tôi cũng gặp phải vấn đề tương tự và sau khi xem xét cẩn thận tất cả các bảng mã và thấy rằng chúng đều ổn, tôi nhận ra rằng thuộc tính bị nghe trộm mà tôi có trong lớp của mình được chú thích là @Column thay vì @JoinColumn (javax.presistence; hibernate) và nó đã phá vỡ mọi thứ.


1

hành hình

show VARIABLES like "%char%”;

tìm ký tự-set-server nếu không phải là utf8mb4.

đặt nó trong my.cnf của bạn, như

vim /etc/my.cnf

thêm một dòng

character_set_server = utf8mb4

cuối cùng khởi động lại mysql


1
character_set_serverlà tùy chọn, KHÔNG PHẢIcharacter-set-server
Arun SR

0

Cài đặt này useOldUTF8Behavior = true hoạt động tốt đối với tôi. Nó không đưa ra lỗi chuỗi không chính xác nhưng nó đã chuyển đổi các ký tự đặc biệt như Ã thành nhiều ký tự và lưu trong cơ sở dữ liệu.

Để tránh những trường hợp như vậy, tôi đã xóa thuộc tính này khỏi tham số JDBC và thay vào đó chuyển đổi kiểu dữ liệu của cột của tôi thành BLOB. Điều này làm việc hoàn hảo.


Bạn có thể vui lòng thêm deatil vào câu trả lời của bạn? (mã, thông thường, v.v.)
aBnormaLz

-2

Bên cạnh đó, kiểu dữ liệu có thể sử dụng cài đặt blob của varchar hoặc text.


Bạn không muốn điều đó
ECostello
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.