JDBC 8.1.7上でのNLS環境におけるキャラクタの整合性の問題について

2001年4月6日 日本オラクル株式会社

[概要]

Java言語においては内部使用キャラクタコードとしてUnicodeが使われています。その為Oracle JDBCドライバは、キャラクタデータをデータベースに挿入する時、またデータベースからデータを取り出す時点で適宜キャラクタ変換を実行します。Java クライアントからJDBCを経由してOracleデータベースにデータを挿入する場合、JavaVMがクライアント側のネイティブコードからJavaのUnicodeの変換を行い、JDBCはデータベースもしくは自身のコード変換機能利用してUnicodeからOracleのデータベースキャラクタセットへの変換を行います。逆にデータを取り出す場合はその反対の事が行われます。

しかしながら、このコード変換の結果いくつかのコード情報が欠落したり、文字化けしてしまう事が有ります。これは、Oracle JDBCのバグでもJava VMのバグでもありません。これは、各ベンダー(Microsoft,Java,Oracleなど)が採用したUnicodeの仕様の解釈の違いに於て生じてしまうのです。この様な差異は、従来のキャラクタセットとUnicodeの間のアプリケーションの互換性を取るためであったり、Unicodeと従来コードセット(JIS,SJIS,EUCJIS)との変換定義が曖昧な為といった理由で発生します。例えばこの問題は同一のOS上(例えばWindowsNT上)でデータ交換が発生した場合にも起こりえます。ただし、幸いな事にこの現象が発生する文字コードはごく極少数のマルチバイトコードのみに限られています。また、Java クライアントからOracleの間では上手くコードが変換が行われていても、SQL*Plusからselect文を実行した場合予期しないコードが返ってくる場合があります。

Oracleでは、NLSにおけるキャラクタの整合性の問題を解決する為に随時作業を行っていますが、Oracleでの作業だけでは、容易に解決できない問題もまだ存在します。この問題の回避策は、問題が起きるコードをあらかじめ把握し、これらのコードがコード変換を利用しない様に回避する事です。Oracleでは、NLS環境でのコード変換に伴う問題を提示するために、『NLS環境におけるキャラクタの整合性の問題』と言う資料を公開しています。問題の詳細に関してはこの資料を参照してください。ただし、この資料はJDBCドライバ8.0.4/8.0.5をベースにして作成されたものであり、Oracle RDBMS及びJDBCドライバのバージョンアップに伴い、これらの問題の状況が変化しています。そのため本資料では、現バージョンのJDBCドライバとデータベースの組み合わせに於てのテストを行いその情報を載せています。ただし、このテスト結果は、あくまでもプログラミングの上での参考資料として日本オラクルが提供しているものであり、日本オラクルがこのテスト環境に於ての動作を推奨している訳でも、この資料の内容を保証している訳でも無い事をご了承ください。

また、このテストはあくまでもJava UnicodeとOracle Unicodeのマッピングの差異を示している物に過ぎません。別のUnicodeアプリケーションやOS(Windowsなど)が入った場合は新しい副作用が引き起こされる場合があります。例えば、\記号問題と言われている物が存在します。このコードは、アメリカでは、バックスラッシュに割り当てられており、C,Java上では制御コード、DOS/Windows上ではディレクトリのセパレータ記号として利用されています。その為これらの言語やOS上で、JISコードの\記号(0x5C)をUnicodeの\記号(U+00A5)にマッピングさせると機能上の非互換性が生じる為、Java,Windows,Oracle上でも通常はバックスラッシュ(U+005C)に割り当てています。クライアントが日本語環境の場合は問題ありませんが、純粋なUnicode環境や他国語環境が混在する場合には\記号が正常に表示されないといった問題が発生する可能性が有ります。こう言ったケースを避ける為に半角の\記号は使用せず、全角の¥記号(0x818F)を使うなどして回避する必要があります。


[テスト環境について]

JDBCドライバを利用したキャラクタの整合性の問題を調査する上で、各環境の組み合わせを考慮する必要が有ります。考慮する必要がある環境としては、Oracle RDBMSサイドでは、データベースバージョンやデータベースキャラクタセット(JA16EUC,JA16SJIS,JA16DBCSなど)があります。また、クライアントサイドでは、JDKのバージョン(1.1.8/1.2.2)及びJava VMのベンダ、ドライバのタイプ(Thin/OCI)、クライアントのOS及びコードセット(WindowsのMS Code page 932、Linux/Solaris上でのEUC JISなど)オブジェクト型利用の有無(一部のデータ型ではデータ変換ロジックが異なります。詳細は『JDBC開発者ガイド』を参照してください。)、JDBCドライバのバージョンなどが考えられます。しかしながらこれらの組み合わせ全てに於てテストを行うとすると数百通りに及ぶ為に現実的に行うのは不可能です。そのため良く使われるであろうと考えられる組み合わせにおいてのみテストを行っています。また、クライアントとServerの文字セットが異なる(SJISからEUC、EUCからSJISへの変換が起きる)場合や外字に対しては、コード対応の問題があるためテストを行っていません。(外字の利用についてはOracle8i NLSガイドのロケール・データのカスタマイズの項目を参照してください。)

テストを行った環境は以下の通りです。

RDBMS Oracle8i R8.1.7.0.0 (Windows/Linux)
JDBCドライバ Oracle JDBC Driver 8.1.7.0.0 Thin/OCI Driver NLS Zip無し

OS/JDK

  • Windows Sun JDK1.1.8-006
  • Windows Sun JDK1.2.2-007
  • Linux IBM JDK1.1.8

    Java ネイティブコード - Oracle NLS Charset
  • Windows MS932 - JA16SJIS
  • Windows SJIS - JA16SJIS
  • Linux EUCJIS - JA16EUC

    ※MS932は、Microsoft Code Page 932の略で、Microsoft Windowsで利用されている日本語キャラクタセットです。基本的にはShift-JISコードのスーパーセットになります(ただし、完全なスーパーセットではありません)。MS Cp932にはShift-JISには含まれていない文字コード(例えば丸つきの1〜20やラテン数字の1から10)が存在しますが、ここでのテストケースには、これらのコードについても行われています。また、MS932-Unicodeのマッピングテーブルは完全に互換ではなく、いわゆる"〜"文字(SJISコード:8160)は、Java上で文字コードとして"SJIS"を選んだ場合はWAVE DASH(Unicode:301C)に変換されるのに対して、"MS932"を選んだ場合は、FULLWIDTH TIDLE(Unicode:FF5E)に変換されます。JDK 1.1.8及び1.2以降では、SJIS-Unicodeの変換を"SJIS"を指定した場合は、JIS規格をベースにした変換マップを、"MS932"を指定した場合は、WindowsNTと互換の変換マップを使って行います。

     ここでは、クライアントとデータベースに書き込まれたキャラクタの相違とJavaプログラムからデータベースに書き込まれたキャラクタをJavaプログラムから呼び出した場合にコードが一致するか(Round Trip)の確認を行います。
     Javaアプリケーションから、あるクライアント上のネイティブコード(例えばSJIS)上のキャラクタセットのある文字(仮に文字のコード番号を"c0"にします。)をデータベースに挿入します。クライアントかJavaにデータが格納される時に、c0は、クライアントコードページからUnicodeに変換されます。この変換は、Java VMが行いますがこの変換されたコードを"u0"とします。次にJavaプログラム内からJDBC経由でコードをデータベースに書き込みます。JDBCの変換プロセスが、Unicode"u0"からデータベースの文字コードに変換を行います(この変換はドライバの種類によってJDBCドライバ本体が行う場合とJDBCドライバマネージャが行う場合、データベースやデータベースクライアントなどで行われる場合があります。)このコードを"c1"とします。反対にデータベースからJava経由でクライアントに文字を表示させる場合は、このコード"c1"が呼び出され、Javaプログラム内に取り込まれます。このコードがUnicode"u1"に変換されます。クライアントにはJava VMがUnicode"u1"からネイティブ文字コード"c2"に変換します。理想的な結果は、c0=c1=c2,u0=u1となる事ですが、そうならないケースも発生します。(ここでは、Javaからネイティブコードへの再変換に関してはテストしません。)

     一般的なテスト結果は以下のパターンが考えられます。

      クライアント Java DB Java (クライアント)  
    正常なコード変換が行われた場合 c0 u0 c0 u0 (c0) (1)
    完全なエラーの場合 c0 u0 c1 u1 (c2) (2)
    ラウンドトリップは成功するが、
    他のクライアントから文字化けする場合
    c0 u0 c1 u0 (c0) (3)
    他のクライアントからは読み出せるが、
    ラウンドトリップに失敗する場合
    c0 u0 c0 u1 (c1) (4)
    Unicode上は同じコードであるが、
    元のネイティブ・コードに戻らない場合
    c0 u0 c1 u0 (c1) (5)

    (1) は理想的なケースです。テスト結果がこの場合は利用に問題がありません。
     大半のコードはここにあてはまります。
    (2) の結果の場合書き込んだ文字が正常に読み出せません。
    (3) の結果は、JavaとOracleのUnicodeとJISのマッピングが異なる為に発生します。Javaクライアントのみで使う場合は問題ありませんが、SQL*Plusや他のクライアントと組み合わせた場合問題が起きます。これは変換テーブルのバグである可能性があります。
    (4) の結果は、複数のUnicodeの文字が単一のJIS文字にマッピングされている場合に発生します。
    (5) の結果は、複数の文字が単一のUnicodeにマッピングされている場合に発生する可能性があります。このケースは実行環境側の問題であるのでここでは考慮しません。


    [問題のキャラクタ]


    このセクションでは、問題が発生したコードについて記述しています。各コードの組み合わせにおいて、表形式で 表記しています。

    クライアント(ネイティブコード) 問題が発生するクライアントの文字コードを示します。
    Java Unicode Javaアプリケーション内で変換されたUnicodeのコード(及び名称)です。
    DB(NLS_CHAR) 問題のJDBCからデータベースに格納された結果の文字コードです
    Java(Round Trip) Unicode DBに書き込まれたコードをJDBCで再びJavaアプリケーション内に読み込んだ時のUnicodeのコード(及び名称)です。
    キャラクタ 該当するキャラクタの字形です。(該当する字形が無い場合は名称を示します。)

    UTF-8の結果テーブル(IE5以上もしくはNetscape 4.75以降以外ではただしく表示されない場合があります。)

    [MS932 - JA16SJIS]

  • JDBC Driver 8.1.7.0.0 OCI/THIN
  • Database Oracle8i R8.1.7.0.0 for Windows NT
  • JDK Sun JDK 1.1.8_006,1.2.2_007 for Windows

    クライアント
    MS932
    Java
    Unicode
    DB
    JA16SJIS
    Java(Round Trip)
    Unicode
    キャラクタ
    8160 U+FF5E
    (FULLWIDTH TILDE)
    8160 U+301C
    (WAVE DASH)

    [SJIS -JA16SJIS]

  • JDBC Driver 8.1.7.0.0 OCI/THIN
  • Database Oracle8i R8.1.7.0.0 for Windows NT
  • JDK Sun JDK 1.1.8_006,1.2.2_007 for Windows

    クライアント
    SJIS
    Java
    Unicode
    DB
    JA16SJIS
    Java(Round Trip)
    Unicode
    キャラクタ
    8191 U+00A2
    (CENT SIGN)
    8191 U+FFE0
    (FULLWIDTH CENT SIGN)
    ¢
    8192 U+00A3
    (POUND SIGN)
    8192 U+FFE1
    (FULLWIDTH POUND SIGN)
    £
    81CA U+00AC
    (NOT SIGN)
    81CA U+FFE2
    (FULLWIDTH NOT SIGN)
    ¬
    8161 U+2016
    (DOUBLE VERTICAL LINE)
    8161 U+2225
    (PARALLEL TO)
    817C U+2212
    (MINUS SIGN)
    817C U+FF0D
    (FULLWIDTH HYPHEN-MINUS)


    [EUCJIS - JA16EUC]

  • JDBC Driver 8.1.7.0.0 OCI/THIN
  • Database Oracle8i R8.1.7.0.0 for Linux
  • JDK IBM JDK 1.1.8 for Linux

    クライアント
    EUCJIS
    Java
    Unicode
    DB
    JA16EUC
    Java(Round Trip)
    Unicode
    キャラクタ
    A1F1 U+00A2 A1A9 U+FF1F
    (FULLWIDTH QUESTION MARK)
    ¢
    A1F2 U+00A3 A1A9 U+FF1F £
    A2CC U+00AC A1A9 U+FF1F ¬
    8FA2B4 U+00AF A1A9 U+FF1F MACRON
    8FA2B1 U+00B8 A1A9 U+FF1F CEDILLA
    8FA9C2 U+0111 A1A9 U+FF1F LATIN SMALL LETTER D WITH STROKE
    8FAAB7 U+0112 8FA9C2 U+0112 LATIN CAPITAL LETTER E WITH MACRON
    8FA2B0 U+02C7 A1A9 U+FF1F CARON
    8FA2AF U+02D8 A1A9 U+FF1F BREVE
    8FA2B2 U+02D9 A1A9 U+FF1F DOT ABOVE
    8FA2B6 U+02DA A1A9 U+FF1F RING ABOVE
    8FA2B5 U+02DB A1A9 U+FF1F OGONEK
    8FA2B3 U+02DD A1A9 U+FF1F DOUBLE ACUTE ACCENT
    8FA2B8 U+0384 A1A9 U+FF1F GREEK TONOS
    8FA2B9 U+0385 A1A9 U+FF1F GREEK DIALYTIKA TONOS
    8FA2B7 U+FF5E A1A9 U+FF1F FULLWIDTH TILDE