Warum verwendet Oracle eine andere Bytelänge als Java für den zusätzlichen Chipmunk mit Unicode-Zeichen?

8

Ich habe Java-Code, der eine UTF-8-Zeichenfolge auf die Größe meiner Oracle-Spalte (11.2.0.4.0) zuschneidet, was zu einem Fehler führt, da Java und Oracle die Zeichenfolge als unterschiedliche Bytelängen betrachten. Ich habe überprüft NLS_CHARACTERSET, ob mein Parameter in Oracle 'UTF8' ist.

Ich habe einen Test geschrieben, der mein Problem unten mit dem Unicode-Chipmunk-Emoji (🐿️) veranschaulicht.

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

Dies schlägt in der letzten Zeile des Tests mit der folgenden Meldung fehl:

ORA-12899: Wert zu groß für Spalte
"MYSCHEMA". "TEMP". "FOO" (aktuell: 9, maximal: 7)

Die Einstellung von NLS_LENGTH_SEMANTICSist BYTE. Leider kann ich dies nicht ändern, da es sich um ein Legacy-System handelt. Ich bin nicht daran interessiert, die Spaltengröße zu erhöhen, sondern nur die Oracle-Größe einer Zeichenfolge zuverlässig vorhersagen zu können.

agradl
quelle
Leider sehe ich im Internet widersprüchliche Berichte darüber, wie viele Bytes dies sein sollte. Manche sagen 7, manche sagen 8, manche sagen 12 (???). Was passiert, wenn Sie das Oracle-Feld als 8 statt 7 deklarieren? Funktioniert es dann? Mir ist klar, dass dies Ihre Frage nach dem Warum nicht explizit beantwortet, aber möglicherweise eine Antwort für Sie gibt.
Jcolebrand

Antworten:

3

Was folgt, ist meine Spekulation.

Java Strings werden intern mit der UTF-16-Codierung dargestellt . Wenn Sie getBytes("UTF-8")Java zwischen den beiden Codierungen konvertieren und wahrscheinlich eine aktuelle Java-Plattform verwenden.

Wenn Sie versuchen, ein Java Stringin der Datenbank zu speichern , führt Oracle auch eine Konvertierung zwischen dem nativen Java-UTF-16 und dem Datenbankzeichensatz durch, wie durch bestimmt NLS_CHARACTERSET.

Der Chipmunk-Charakter wurde 2014 als Teil des Unicode-Standards genehmigt (gemäß der von Ihnen verlinkten Seite), während die neueste Version von Oracle 11g rel.2 2013 veröffentlicht wurde .

Man könnte annehmen, dass Oracle einen anderen oder veralteten Zeichenkonvertierungsalgorithmus verwendet, sodass die Bytedarstellung von 🐿️) auf dem Server (9 Byte lang) anders ist als getBytes()die auf dem Client zurückgegebene (7 Byte).

Um dieses Problem zu beheben, können Sie Ihren Oracle-Server aktualisieren oder UTF-16 als Datenbankzeichensatz verwenden.

mustaccio
quelle
Damit war das Problem behoben. Mein Orakel 11g verwendete jdk 1.6.0_141, während die 12-Instanz jdk 1.8.0_121
agradl
3
Bitte markieren Sie die Frage als beantwortet, damit die nächste Person weiß, dass dies funktioniert hat :)
jcolebrand
Ich habe zu früh gesprochen, ich untersuche weiter, um meinen Verdacht zu bestätigen - es hatte nichts mit der
Orakelversion zu tun
1

Das Problem liegt in der Behandlung von zusätzlichen Unicode-Zeichen durch Oracle, wenn dies der Fall NLS_LENGTH_SEMANTICSist UTF8.

Aus der Dokumentation (Hervorhebung hinzugefügt).

Der UTF8-Zeichensatz codiert Zeichen in einem, zwei oder drei Bytes. Es ist für ASCII-basierte Plattformen.

Zusätzliche Zeichen, die in eine UTF8-Datenbank eingefügt werden, beschädigen die Daten in der Datenbank nicht. Ein zusätzliches Zeichen wird als zwei separate, benutzerdefinierte Zeichen behandelt, die 6 Byte belegen. Oracle empfiehlt, dass Sie zu AL32UTF8 wechseln, um zusätzliche Zeichen im Datenbankzeichensatz vollständig zu unterstützen.

Darüber hinaus ist der letzte Codepunkt in der Eichhörnchenzeichenfolge ein Variationsselektor und optional. Ich habe dies mit einem Unicode-Zeicheninspektor gesehen

Nach dem Ändern des Datenbankparameters NLS_CHARACTERSETin AL32UTF8den Test bestanden.

agradl
quelle