補足のユニコード文字シマリスにOracleがJavaとは異なるバイト長を使用するのはなぜですか?


8

JavaコードでUTF-8文字列をOracle(11.2.0.4.0)列のサイズにトリミングすると、JavaとOracleは文字列を異なるバイト長として認識するため、エラーが発生します。NLS_CHARACTERSETOracleのパラメーターが「UTF8」であることを確認しました。

ユニコードシマリス絵文字を使用して、以下の問題を説明するテストを書きました(🐿️)

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

これはテストの最後の行で失敗し、次のメッセージが表示されます。

ORA-12899:列
"MYSCHEMA"。 "TEMP"。 "FOO"の値が大きすぎます(実際:9、最大:7)

の設定はNLS_LENGTH_SEMANTICSですBYTE。残念ながら、これはレガシーシステムなので変更できません。列サイズの増加には興味がなく、文字列のOracleサイズを確実に予測できます。


悲しいことに、これは何バイトあるべきかについて、インターネット上で矛盾するレポートを目にしています。7と言う人、8と言う人、12と言う人(???)。Oracleフィールドを7ではなく8と宣言するとどうなりますか?私はそれが理由に関するあなたの質問に明確に答えることはありませんが、それはあなたのためにいくつかの答えを与えるかもしれません。
jcolebrand

回答:


3

以下は私の推測です。

Java String内部的にUTF-16エンコーディングを使用して表されます。いつgetBytes("UTF-8")のJava 2つの符号化方式との間の変換、およびあなたはおそらく、最新のJavaプラットフォームを使用します。

Java Stringをデータベースに格納しようとすると、Oracleは、JavaネイティブUTF-16とデータベースの文字セットとの間の変換も実行しますNLS_CHARACTERSET

シマリス文字は、リンクしたページに従って2014年にUnicode標準の一部として承認されました、Oracle 11g rel.2の最新リリースは2013年に公開されました

Oracleが異なるまたは古い文字変換アルゴリズムを使用しているため、サーバー(長さ9バイト)の🐿️)のバイト表現がgetBytes()クライアントで返されるもの(7バイト)とは異なると想定する人もいます。

この問題を解決するには、Oracleサーバーをアップグレードするか、データベースの文字セットとしてUTF-16を使用できます。


これで問題は解決しました。私のOracle 11gはjdk 1.6.0_141を使用していましたが、12インスタンスはjdk 1.8.0_121を使用しています
agradl

3
次の人がこれがうまくいったことを知っているように、質問に回答済みのマークを付けてください:)
jcolebrand

それはOracleのバージョンに関連していなかった...お楽しみに-私は私の疑いを確認するために、さらに調査しています、あまりにもすぐに話を聞いた
agradl

1

問題は、がである場合のOracleの補足Unicode文字の処理にNLS_LENGTH_SEMANTICSありUTF8ます。

ドキュメントから(強調を追加)。

UTF8文字セットは、文字を1、2、または3バイトでエンコードします。ASCIIベースのプラットフォーム用です。

UTF8データベースに挿入された補足文字は、データベース内のデータを破損しません。補助文字は、6バイトを占める2つの個別のユーザー定義文字として扱われます。データベースキャラクタセットの補助文字を完全にサポートするには、AL32UTF8に切り替えることをお勧めします。

さらに、リス文字列の最後のコードポイントはバリエーションセレクターであり、オプションです。私はこれをユニコード文字インスペクターを使って見ました

データベースのNLS_CHARACTERSETパラメータを変更しAL32UTF8てテストに合格した後。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.