Mengapa Oracle menggunakan byte panjang yang berbeda dari java untuk chipmunk karakter unicode tambahan?

8

Saya memiliki kode java pemangkasan string UTF-8 dengan ukuran kolom Oracle (11.2.0.4.0) saya yang akhirnya menimbulkan kesalahan karena java dan Oracle melihat string sebagai panjang byte yang berbeda. Saya telah memverifikasi NLS_CHARACTERSETparameter saya di Oracle adalah 'UTF8'.

Saya menulis tes yang menggambarkan masalah saya di bawah ini menggunakan emoji unicode chipmunk (🐿️)

public void test() throws UnsupportedEncodingException, SQLException {
    String squirrel = "\uD83D\uDC3F\uFE0F";
    int squirrelByteLength = squirrel.getBytes("UTF-8").length; //this is 7
    Connection connection = dataSource.getConnection();

    connection.prepareStatement("drop table temp").execute();

    connection.prepareStatement("create table temp (foo varchar2(" + String.valueOf(squirrelByteLength) + "))").execute();

    PreparedStatement statement = connection.prepareStatement("insert into temp (foo) values (?)");
    statement.setString(1, squirrel);
    statement.executeUpdate();
}

Ini gagal pada baris terakhir tes dengan pesan berikut:

ORA-12899: nilai terlalu besar untuk kolom
"MYSCHEMA". "TEMP". "FOO" (aktual: 9, maksimum: 7)

Pengaturan NLS_LENGTH_SEMANTICSadalah BYTE. Sayangnya, saya tidak dapat mengubah ini karena ini adalah sistem warisan. Saya tidak tertarik untuk meningkatkan ukuran kolom, hanya dapat dipercaya mampu memprediksi ukuran string Oracle.

agradl
sumber
Sayangnya saya melihat laporan yang saling bertentangan di internet tentang berapa byte yang seharusnya. Ada yang bilang 7, ada yang bilang 8, ada yang bilang 12 (???). Apa yang terjadi jika Anda mendeklarasikan bidang Oracle sebagai 8 alih-alih 7. Apakah itu berfungsi kemudian? Saya menyadari bahwa itu tidak secara eksplisit menjawab pertanyaan Anda tentang mengapa tetapi mungkin memberikan beberapa jawaban untuk Anda.
jcolebrand

Jawaban:

3

Berikut ini adalah spekulasi saya.

Java Strings diwakili secara internal menggunakan UTF-16 encoding . Ketika Anda getBytes("UTF-8")Java mengkonversi antara dua pengkodean, dan Anda mungkin menggunakan platform Java yang terbaru.

Ketika Anda mencoba untuk menyimpan Java Stringdalam database, Oracle juga melakukan konversi antara Java asli UTF-16 dan karakter basis data yang ditentukan oleh NLS_CHARACTERSET.

Karakter chipmunk disetujui sebagai bagian dari standar Unicode pada tahun 2014 (sesuai dengan halaman yang Anda tautkan), sementara rilis terbaru Oracle 11g rel.2 diterbitkan pada tahun 2013 .

Orang mungkin berasumsi bahwa Oracle menggunakan algoritma konversi karakter yang berbeda atau usang sehingga representasi byte 🐿️) di server (panjang 9 byte) berbeda dari apa yang getBytes()dikembalikan pada klien (7 byte).

Saya kira untuk mengatasi masalah ini Anda dapat memutakhirkan server Oracle Anda atau menggunakan UTF-16 sebagai set karakter basis data.

mustaccio
sumber
Itu menyelesaikan masalah. Oracle 11g saya menggunakan jdk 1.6.0_141 sementara 12 instance menggunakan jdk 1.8.0_121
agradl
3
Harap tandai pertanyaan sebagai dijawab agar orang berikutnya tahu ini berhasil :)
jcolebrand
Saya berbicara terlalu cepat, saya sedang menyelidiki lebih lanjut untuk mengkonfirmasi kecurigaan saya - itu tidak terkait dengan versi oracle ... tetap
disini
1

Masalahnya adalah ketika Oracle menangani karakter unicode tambahan saat NLS_LENGTH_SEMANTICSitu UTF8.

Dari dokumentasi (penekanan ditambahkan).

Set karakter UTF8 mengkodekan karakter dalam satu, dua, atau tiga byte. Ini untuk platform berbasis ASCII.

Karakter tambahan yang dimasukkan ke dalam basis data UTF8 tidak merusak data di dalam basis data. Karakter tambahan diperlakukan sebagai dua karakter terpisah yang ditentukan pengguna yang menempati 6 byte. Oracle merekomendasikan Anda beralih ke AL32UTF8 untuk dukungan penuh karakter tambahan dalam rangkaian karakter basis data.

Selain itu, titik kode terakhir dalam string tupai adalah pemilih variasi dan opsional. Saya melihat ini menggunakan inspektur karakter unicode

Setelah mengubah NLS_CHARACTERSETparameter database untuk AL32UTF8tes lulus.

agradl
sumber