Pourquoi Oracle utilise-t-il une longueur d'octet différente de java pour le tamia supplémentaire à caractères unicode?


8

J'ai du code java qui coupe une chaîne UTF-8 à la taille de ma colonne Oracle (11.2.0.4.0), ce qui finit par générer une erreur car java et Oracle voient la chaîne comme des longueurs d'octets différentes. J'ai vérifié que mon NLS_CHARACTERSETparamètre dans Oracle est 'UTF8'.

J'ai écrit un test qui illustre mon problème ci-dessous en utilisant les 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();
}

Cela échoue sur la dernière ligne du test avec le message suivant:

ORA-12899: valeur trop grande pour la colonne
"MYSCHEMA". "TEMP". "FOO" (réel: 9, maximum: 7)

Le réglage de NLS_LENGTH_SEMANTICSest BYTE. Malheureusement, je ne peux pas changer cela car c'est un système hérité. Je ne suis pas intéressé à augmenter la taille de la colonne, mais à pouvoir prédire de manière fiable la taille Oracle d'une chaîne.


Malheureusement, je vois sur Internet des rapports contradictoires sur le nombre d'octets que cela devrait être. Certains disent 7, certains disent 8, certains disent 12 (???). Que se passe-t-il si vous déclarez le champ Oracle à 8 au lieu de 7. Cela fonctionne-t-il alors? Je sais que cela ne répond pas explicitement à votre question de savoir pourquoi, mais cela peut vous donner une réponse.
jcolebrand

Réponses:


3

Ce qui suit est ma spéculation.

Les Java Stringsont représentés en interne à l'aide du codage UTF-16 . Lorsque vous getBytes("UTF-8")convertissez Java entre les deux encodages, et que vous utilisez probablement une plate-forme Java à jour.

Lorsque vous essayez de stocker un Java Stringdans la base de données, Oracle effectue également une conversion entre l'UTF-16 natif Java et le jeu de caractères de la base de données, comme déterminé par NLS_CHARACTERSET.

Le caractère tamia a été approuvé dans le cadre de la norme Unicode en 2014 (selon la page que vous avez liée), tandis que la dernière version d'Oracle 11g rel.2 a été publiée en 2013 .

On pourrait supposer qu'Oracle utilise un algorithme de conversion de caractères différent ou obsolète, de sorte que la représentation en octets de 🐿️) sur le serveur (9 octets de long) est différente de celle qui getBytes()revient sur le client (7 octets).

Je suppose que pour résoudre ce problème, vous pouvez mettre à niveau votre serveur Oracle ou utiliser UTF-16 comme jeu de caractères de base de données.


Cela a résolu le problème. Mon oracle 11g utilisait jdk 1.6.0_141 tandis que l'instance 12 utilise jdk 1.8.0_121
agradl

3
Veuillez marquer la question comme répondue pour que la prochaine personne sache que cela a fonctionné :)
jcolebrand

J'ai parlé trop tôt,
j'enquête

1

Le problème est avec la gestion par Oracle des caractères Unicode supplémentaires quand NLS_LENGTH_SEMANTICSc'est UTF8.

À partir de la documentation (emphase ajoutée).

Le jeu de caractères UTF8 code les caractères sur un, deux ou trois octets. C'est pour les plateformes basées sur ASCII.

Les caractères supplémentaires insérés dans une base de données UTF8 ne corrompent pas les données de la base de données. Un caractère supplémentaire est traité comme deux caractères distincts définis par l'utilisateur qui occupent 6 octets. Oracle vous recommande de passer à AL32UTF8 pour une prise en charge complète des caractères supplémentaires dans le jeu de caractères de la base de données.

En outre, le dernier point de code dans la chaîne d'écureuil est un sélecteur de variation et facultatif. J'ai vu cela en utilisant un inspecteur de caractères Unicode

Après avoir changé le NLS_CHARACTERSETparamètre de la base de données en AL32UTF8test réussi.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.