Perché Oracle utilizza una lunghezza di byte diversa da java per il chipmunk di carattere unicode supplementare?


8

Ho un codice Java che taglia una stringa UTF-8 alla dimensione della mia colonna Oracle (11.2.0.4.0) che finisce per generare un errore perché Java e Oracle vedono la stringa come lunghezze di byte diverse. Ho verificato che il mio NLS_CHARACTERSETparametro in Oracle è "UTF8".

Ho scritto un test che illustra il mio problema di seguito utilizzando l' emoji chipmunk unicode (🐿️)

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

Questo non riesce sull'ultima riga del test con il seguente messaggio:

ORA-12899: valore troppo grande per la colonna
"MYSCHEMA". "TEMP". "FOO" (effettivo: 9, massimo: 7)

L'impostazione di NLS_LENGTH_SEMANTICSè BYTE. Sfortunatamente, non posso cambiarlo perché è un sistema legacy. Non mi interessa aumentare le dimensioni della colonna, potendo semplicemente prevedere la dimensione Oracle di una stringa.


Purtroppo sto vedendo rapporti contrastanti su Internet su quanti byte dovrebbero essere. Alcuni dicono 7, alcuni dicono 8, altri dicono 12 (???). Cosa succede se si dichiara il campo Oracle come 8 invece di 7. Funziona allora? Mi rendo conto che non risponde esplicitamente alla tua domanda sul perché, ma può darti una risposta.
jcolebrand

Risposte:


3

Ciò che segue è la mia speculazione.

I Java Stringsono rappresentati internamente usando la codifica UTF-16 . Quando getBytes("UTF-8")Java converte tra le due codifiche e probabilmente si utilizza una piattaforma Java aggiornata.

Quando si tenta di archiviare un Java Stringnel database, Oracle esegue anche la conversione tra UTF-16 nativo Java e il set di caratteri del database come determinato da NLS_CHARACTERSET.

Il personaggio chipmunk è stato approvato come parte dello standard Unicode nel 2014 (secondo la pagina che hai collegato), mentre l'ultima versione di Oracle 11g rel.2 è stata pubblicata nel 2013 .

Si potrebbe supporre che Oracle utilizzi un algoritmo di conversione dei caratteri diverso o obsoleto, quindi la rappresentazione in byte di 🐿️) sul server (lunga 9 byte) è diversa da quella che getBytes()restituisce sul client (7 byte).

Immagino che per risolvere questo problema potresti aggiornare il tuo server Oracle o utilizzare UTF-16 come set di caratteri del database.


Ciò ha risolto il problema. Il mio oracolo 11g utilizzava jdk 1.6.0_141 mentre la 12 istanza utilizza jdk 1.8.0_121
agradl

3
Si prega di contrassegnare la domanda come risposta in modo che la prossima persona sappia che ha funzionato :)
jcolebrand

Ho parlato troppo presto, sto indagando ulteriormente per confermare il mio sospetto - non era correlato alla versione dell'oracolo ... rimanete sintonizzati
agradl

1

Il problema è con la gestione di Oracle di caratteri Unicode supplementari quando NLS_LENGTH_SEMANTICSè UTF8.

Dalla documentazione (enfasi aggiunta).

Il set di caratteri UTF8 codifica i caratteri in uno, due o tre byte. È per piattaforme basate su ASCII.

I caratteri supplementari inseriti in un database UTF8 non corrompono i dati nel database. Un carattere supplementare viene trattato come due caratteri separati definiti dall'utente che occupano 6 byte. Oracle consiglia di passare a AL32UTF8 per il pieno supporto di caratteri supplementari nel set di caratteri del database.

Inoltre, l'ultimo punto di codice nella stringa di scoiattolo è un selettore di variazione e facoltativo. Ho visto questo usando un ispettore di caratteri unicode

Dopo aver modificato il NLS_CHARACTERSETparametro del database al AL32UTF8test superato.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.