7 Unicodeを使用したプログラミング
この章では、Oracleのデータベースのプログラミングおよびアクセス製品とUnicodeを併用する方法について説明します。この章のトピックは、次のとおりです:
7.1 Unicodeを使用したプログラミングの概要
Oracleには、Unicodeデータの挿入と取出しを行うためのデータベース・アクセス製品がいくつか用意されています。Oracleでは、JavaやC/C++などの一般的に使用されているプログラミング環境に対応したデータベース・アクセス製品を提供しています。データはデータベースとクライアント・プログラム間で透過的に変換されるため、クライアント・プログラムがデータベース文字セットと各国語文字セットの影響を受けないことが保証されます。さらに、クライアント・プログラムは、データベースで使用されるNCHAR
やCHAR
などの文字データ型の影響も受けない場合があります。
データ変換操作によってデータベース・サーバーに負荷をかけないように、Oracleは常に、これらの操作をクライアント側のデータベース・アクセス製品に移動しようとします。データベースでデータ変換が必要な場合があり、この操作はパフォーマンスに影響します。この章では、データ変換の流れの詳細を説明します。
7.1.1 データベース・アクセス製品のスタックおよびUnicode
Oracleは総合的なデータベース・アクセス製品を提供しています。これらの製品によって、データベースに格納されているUnicodeデータに異なる開発環境のプログラムでアクセスできます。次の表に、これらの製品を示します。
表7-1 Oracleのデータベース・アクセス製品
プログラミング環境 | Oracleのデータベース・アクセス製品 |
---|---|
C/C++ |
Oracle Call Interface(OCI) Oracle Pro*C/C++ Oracle ODBCドライバ Oracle Provider for OLE DB Oracle Data Provider for .NET。 |
Java |
Oracle JDBC OCIまたはThinドライバ Oracleサーバー側Thinドライバ Oracleサーバー側内部ドライバ |
PL/SQL |
Oracle PL/SQLおよびSQL |
Visual Basic/C# |
Oracle ODBCドライバ Oracle Provider for OLE DB |
次の図に、データベース・アクセス製品によるデータベースへのアクセス方法を示します。
Oracle Call Interface(OCI)は、残りのクライアント側データベース・アクセス製品で使用される最下位レベルのAPIです。SQL CHAR
データ型およびNCHAR
データ型で格納されているUnicodeデータにC/C++プログラムでアクセスするための、柔軟な方法を提供します。OCIを使用して、データの挿入または取出しに使用する文字セット(UTF-8やUTF-16など)をプログラムで指定できます。データベースには、Oracle Netを介してアクセスします。
Oracle Pro*C/C++を使用すると、プログラム内にSQLおよびPL/SQLを埋め込むことができます。OCIのUnicode機能を使用して、SQL CHAR
データ型およびNCHAR
データ型のUTF-16およびUTF-8データへのアクセスを提供します。
Oracle ODBCドライバを使用すると、Windowsプラットフォームで実行されているC/C++、Visual BasicおよびVBScriptプログラムで、データベースにSQL CHAR
データ型およびNCHAR
データ型で格納されているUnicodeデータにアクセスできます。ODBC標準仕様で指定されているSQLWCHAR
インタフェースを実装することで、UTF-16データへのアクセスを提供します。
Oracle Provider for OLE DBを使用すると、Windowsプラットフォームで実行されているC/C++、Visual BasicおよびVBScriptプログラムで、SQL CHAR
データ型およびNCHAR
データ型で格納されているUnicodeデータにアクセスできます。ワイド文字列のOLE DBデータ型を介してUTF-16データへのアクセスを提供します。
Oracle Data Provider for .NETを使用すると、Windowsプラットフォーム上の任意の.NETプログラミング環境で実行されているプログラムで、SQL CHAR
データ型およびNCHAR
データ型で格納されているUnicodeデータにアクセスできます。Unicodeのデータ型を介してUTF-16データへのアクセスを提供します。
Oracle JDBCドライバは、Oracle Databaseにアクセスするための主要なJavaプログラム・インタフェースです。Oracle JDBCドライバを次に示します。
-
純粋なJavaドライバで、Javaアプレットで主に使用され、TCP/IPを介したOracle NetプロトコルをサポートするJDBC Thinドライバ
-
純粋なJavaドライバで、Javaストアド・プロシージャ内で他のOracleサーバーへの接続に使用されるサーバー側JDBC Thinドライバ
ドライバはすべて、データベースに格納されているSQL CHAR
データ型およびNCHAR
データ型へのUnicodeデータ・アクセスをサポートします。
PL/SQLとSQLのエンジンは、OCIなどのクライアント側プログラムやサーバー側のPL/SQLストアド・プロシージャのかわりに、PL/SQLプログラムとSQL文を処理します。これらのエンジンを使用すると、PL/SQLプログラムでCHAR
、VARCHAR2
、NCHAR
およびNVARCHAR2
の各変数を宣言し、データベースのSQL CHAR
およびNCHAR
データ型にアクセスできます。
次の項では、各データベース・アクセス製品が、Oracle DatabaseへのUnicodeデータ・アクセスをどのようにサポートしているかを説明し、これらの製品の使用例を示します。
7.2 Unicodeを使用したSQLとPL/SQLのプログラミング
SQLは、すべてのプログラムとユーザーが、Oracle Databaseのデータに直接的または間接的にアクセスするときに使用する基本言語です。PL/SQLは、SQLのデータ操作能力と手続き型言語のデータ処理能力を結合した手続き型言語です。SQLとPL/SQLは両方とも、他のプログラム言語に埋め込むことができます。この項では、多言語アプリケーションにデプロイ可能なSQLとPL/SQLのUnicode関連機能について説明します。
この項では、次の項目について説明します。
7.2.1 SQL NCHARデータ型
SQL NCHAR
データ型は次の3つです。
7.2.1.1 NCHARデータ型
表の列またはPL/SQL変数をNCHAR
データ型として定義する場合、長さは常に文字数として指定します。たとえば次の文では、最大文字長が30の列が1つ作成されます。
CREATE TABLE table1 (column1 NCHAR(30));
この列の最大バイト数は、次のように決定されます。
maximum number of bytes = (maximum number of characters) x (maximum number of bytes for each character)
たとえば、各国語文字セットがUTF8の場合、最大バイト長は30文字に1文字当たり3バイトを乗算した値、つまり90バイトとなります。
すべてのNCHAR
データ型に使用する各国語文字セットは、データベースの作成時に定義します。各国語文字セットはUTF8またはAL16UTF16のいずれかです。デフォルトはAL16UTF16です。
使用可能な最大列サイズは、各国語文字セットがUTF8の場合は32000文字、AL16UTF16の場合は8000文字です。実際のデータが最大バイト制限の16000の対象となります。2つのサイズ制限は同時に満たす必要があります。PL/SQLでは、NCHAR
データの最大長は32767バイトです。NCHAR
変数は32767文字まで定義できますが、実際のデータは32767バイトを超えることはできません。列の長さより短い値を挿入すると、最大文字長と最大バイト長のいずれか小さいほうの値まで空白で埋められます。
ノート:
UTF8は可変幅文字セットであるため、パフォーマンスに影響する場合があります。NCHAR
フィールドの空白埋めが過剰に行われると、パフォーマンスが低下します。NVARCHAR2
データ型を使用するか、NCHAR
データ型の文字セットをAL16UTF16に変更することを考慮してください。
7.2.1.2 NVARCHAR2データ型
NVARCHAR2
データ型は、各国語文字セットを使用する可変長文字列を指定します。NVARCHAR2
列を使用して表を作成するときは、列の最大文字数を指定します。NVARCHAR2
の長さは、NCHAR
の場合と同様に常に文字単位です。続いて、値が列の最大長を超えないかぎり、ユーザーが指定したとおりに各値が列内に格納されます。文字列の値が最大長まで埋め込まれることはありません。
NVARCHAR2
列の最大長は、MAX_STRING_SIZE
=
STANDARD
のときに4000文字であり、MAX_STRING_SIZE
=
EXTENDED
のときに32767文字です。これらの長さは、UTF8を使用した場合に基づいており値は2000文字になりますが、AL16UTF16を使用した場合には16383文字になります。
PL/SQLでは、NVARCHAR2
変数の最大長は32767バイトです。NVARCHAR2
変数は32767文字まで定義できますが、実際のデータは32767バイトを超えることはできません。
次の文では、最大文字長が2000で最大バイト長が4000のNVARCHAR2
列を1つ含む表が作成されます。
CREATE TABLE table2 (column2 NVARCHAR2(2000));
7.2.1.3 NCLOBデータ型
NCLOB
は、最大4GBのUnicode文字を格納する、キャラクタ・ラージ・オブジェクトです。BLOB
データ型とは異なり、NCLOB
データ型では完全なトランザクション・サポートがあるため、SQL、DBMS_LOB
パッケージまたはOCIを使用して行われた変更は、完全にトランザクションに組み込まれます。NCLOB
値の操作は、コミットおよびロールバックできます。ただし、あるトランザクションでPL/SQLまたはOCI変数に保存したNCLOB
ロケータを別のトランザクションまたはセッションで使用することはできません。
NCLOB
値は、各国語文字セットに関係なく、UCS-2互換形式でデータベースに格納されます。格納されたUnicode値は、クライアントまたはサーバーで要求された固定幅または可変幅の文字セットに変換されます。可変幅の文字セットを使用してNCLOB
列に挿入したデータは、データベースに格納される前にUCS-2互換形式に変換されます。
関連項目:
NCLOB
データ型の詳細は、『Oracle Database SecureFilesおよびラージ・オブジェクト開発者ガイド』を参照してください
7.2.2 NCHARデータ型と他のデータ型の間の暗黙的な変換
Oracleでは、SQL NCHAR
データ型と、他のOracleデータ型(CHAR
、VARCHAR2
、NUMBER
、DATE
、ROWID
およびCLOB
など)の間の暗黙的な変換がサポートされています。SQL NCHAR
データ型については、CHAR
およびVARCHAR2
データ型に関する暗黙的な変換もサポートされています。SQL NCHAR
データ型は、SQL CHAR
データ型と同様に使用できます。
SQL CHAR
データ型とSQL NCHAR
データ型の間の型変換では、データベース文字セットと各国語文字セットが異なるときに、文字セットの変換が行われる場合があります。ターゲット・データがCHAR
またはNCHAR
の場合は、空白埋めが発生することがあります。
7.2.3 データ型変換中のデータ消失に対する例外処理
文字セット変換が必要なときは、データ型変換時にデータを消失する可能性があります。ソース文字セット内の文字がターゲットの文字セット内で定義されていない場合、その場所には置換文字が使用されます。たとえば、NCHAR
データを通常のCHAR
列に挿入しようとしたときに、NCHAR
(Unicode)フォーム内の文字データがデータベース文字セットに変換できない場合、その文字はデータベース文字セットで定義されている置換文字に置き換えられます。文字列型変換時のデータ消失に関する対処方法は、NLS_NCHAR_CONV_EXCP
初期化パラメータによって制御されます。このパラメータがTRUE
に設定されているときは、データ消失が発生するSQL文はORA-12713
エラーを戻し、対応する操作は停止します。このパラメータがFALSE
に設定されているときは、データ消失はレポートされず、変換不可能な文字は置換文字で置き換えられます。デフォルト値はFALSE
です。このパラメータは、暗黙的な変換と明示的な変換の両方に対して機能します。
PL/SQLの場合は、SQL CHAR
データ型とNCHAR
データ型の変換時にデータ消失が発生すると、暗黙的な変換の場合も明示的な変換の場合もLOSSY_CHARSET_CONVERSION
例外が発生します。
7.2.4 暗黙的なデータ型変換の規則
状況によって、一方向のデータ型変換のみが可能な場合と、両方向のデータ型変換が可能な場合があります。Oracleでは、データ型間の変換に関して一連の規則が定義されています。次の表に、データ型間の変換に関する規則を示します。
表7-2 データ型間の変換に関する規則
文 | 規則 |
---|---|
|
値は、ターゲット・データベース列のデータ型に変換されます。 |
|
データベースのデータは、ターゲット変数のデータ型に変換されます。 |
変数の代入 |
等号の右辺の値は、等号の左辺のターゲット変数のデータ型に変換されます。 |
SQL関数とPL/SQL関数のパラメータ |
|
連結(||)操作または |
あるオペランドがSQL |
SQL |
文字の値は |
SQL |
文字の値は |
SQL |
文字の値は |
SQL |
SQL
SQL |
7.2.5 Unicodeデータ型のSQL関数
SQL NCHAR
データ型は、明示的な変換関数を使用して、SQL CHAR
データ型や他のデータ型と相互に変換できます。この項の例では、次の文で作成された表を使用します。
CREATE TABLE customers (id NUMBER, name NVARCHAR2(50), address NVARCHAR2(200), birthdate DATE);
関連項目:
SQL NCHARデータ型の明示的な変換関数の詳細は、『Oracle Database SQL言語リファレンス』
を参照してください。
例7-1 TO_NCHAR関数を使用したcustomers表の移入
TO_NCHAR
関数は実行時にデータを変換しますが、N
関数はコンパイル時にデータを変換します。
INSERT INTO customers VALUES (1000, TO_NCHAR('John Smith'),N'500 Oracle Parkway',sysdate);
例7-2 TO_CHAR関数を使用したcustomers表からの選択
次の文では、name
の値が各国語文字セットの文字からデータベース文字セットの文字に変換され、その後でLIKE
句に従って選択されます。
SELECT name FROM customers WHERE TO_CHAR(name) LIKE '%Sm%';
次の出力が表示されるはずです。
NAME -------------------------------------- John Smith
例7-3 TO_DATE関数を使用したcustomers表からの選択
N
関数を使用すると、NCHAR
またはCHAR
データ型のデータをTO_DATE
関数のパラメータとして渡すことができます。データ型変換は実行時に行われるため、データ型を混在させることができます。
DECLARE ndatestring NVARCHAR2(20) := N'12-SEP-1975'; ndstr NVARCHAR2(50); BEGIN SELECT name INTO ndstr FROM customers WHERE (birthdate)> TO_DATE(ndatestring, 'DD-MON-YYYY', NLS_DATE_LANGUAGE = 'AMERICAN'); END;
例7-3に示したように、SQL NCHAR
データ型のデータを明示的な変換関数に渡すことができます。複数の文字列パラメータを使用すると、SQL CHAR
およびNCHAR
データ型のデータを混在させることができます。
7.2.6 その他のSQL関数
大部分のSQL関数は、SQL NCHAR
データ型および混合した文字データ型の引数を取ることができます。戻されるデータ型は、最初の引数の型に基づきます。これらの関数に渡されたNUMBER
やDATE
などの非文字列データ型は、VARCHAR2
に変換されます。次の例では、「Unicodeデータ型のSQL関数」で作成したcustomers
表を使用します。
例7-4 INSTR関数
この例では、文字列リテラル'Sm'
がNVARCHAR2
に変換されてから、INSTR
によってスキャンされ、name
で最初に出現するこの文字列の位置が検出されます。
SELECT INSTR(name, N'Sm', 1, 1) FROM customers;
例7-5 CONCAT関数
SELECT CONCAT(name,id) FROM customers;
id
はNVARCHAR2
に変換された後、name
と連結されます。
7.2.7 Unicode文字列リテラル
Unicode文字列リテラルは、次の方法でSQLおよびPL/SQLに入力できます。
-
引用符で囲まれた文字列リテラルの前に接頭辞
N
を付加します。この接頭辞によって、その後に続く文字列リテラルがNCHAR
文字列リテラルであることが明示的に示されます。たとえば、N'résumé'
はNCHAR
文字列リテラルです。このメソッドの制限事項の詳細は、「NCHAR文字列リテラルの置換」を参照してください。 -
NCHR(
n
)
SQL関数を使用します。この関数は、各国語文字セット(AL16UTF16またはUTF8)内の文字コードの数を戻します。複数のNCHR(
n
)
関数を連結した結果は、NVARCHAR2
データになります。このように、クライアントとサーバーの文字セット変換を行わずに、NVARCHAR2
文字列を直接作成できます。たとえば、NCHR(32)
は空白文字を表します。NCHR(
n
)
は同じ各国語文字セットに関連付けられているため、結果値の移植性はその各国語文字セットで実行されるアプリケーションに限定されます。この移植性が重要な場合は、UNISTR
関数を使用して移植性の制限を解除します。 -
UNISTR
('string'
) SQL関数を使用します。UNISTR
('string'
)は、文字列を各国語文字セットに変換します。移植性を確保してデータを保持するには、ASCII文字と\xxxx
形式のUnicodeエンコーディングのみを含めます。xxxx
は、UTF-16エンコーディング形式で文字コード値を表す16進値です。たとえば、UNISTR('G\0061ry')
は'Gary'
を表します。ASCII文字は、データベース文字セットに変換されてから、各国語文字セットに変換されます。Unicodeエンコーディングは、各国語文字セットに直接変換されます。
最後の2つの方法を使用すると、すべてのUnicode文字列リテラルをエンコードできます。
7.2.8 NCHAR文字列リテラルの置換
この項では、NCHAR
文字列リテラル置換の実行時にデータの損失を防ぐ方法を説明します。
リテラルのテキストがSQLまたはPL/SQL文の一部になると、接頭辞N
の有無にかかわらず、残りの文と同じ文字セットでエンコードされます。クライアント側では、文にクライアント文字セットが使用されます。これは、NLS_LANG
で定義されているクライアント文字セットにより決定されるか、OCIEnvNlsCreate()
コールで指定されるか、またはJDBCでUTF-16として事前定義されます。サーバー側では、文はデータベース文字セットです。
-
SQLまたはPL/SQL文がクライアントからデータベース・サーバーに送信されると、それに応じて文字セットが変換されます。データベース文字セットに、テキスト・リテラルで使用されるキャラクタの一部が含まれない場合、変換時にデータが失われるので、注意が必要です。この問題は、
CHAR
テキスト・リテラルよりもNCHAR
文字列リテラルにより大きな影響を及ぼします。これは、N'
リテラルがデータベース文字セットに依存しないよう設計されており、クライアント文字セットがサポートするデータを提供できる必要があるためです。互換性のないデータベース文字セットへの変換時にデータの損失を防ぐため、
NCHAR
リテラル置換機能をアクティブにできます。この機能では、クライアント側でN'
リテラルが内部書式により透過的に置換されます。データベース・サーバーは、文の実行時にこのリテラルをUnicodeにデコードします。 -
「OCIでのSQL NCHAR文字列リテラルの処理」および「JDBCでのSQL NCHAR文字列リテラルの使用」では、OCIおよびJDBCのそれぞれで置換機能を有効にする方法を示しています。SQL*Plusなどの多くのアプリケーションでは、データベースへの接続にOCIを使用し、
NCHAR
リテラル置換を明示的に制御しないため、クライアント環境変数ORA_NCHAR_LITERAL_REPLACE
をTRUE
に設定してこの機能を制御できます。デフォルトでは、この機能は、下位互換性を維持するために無効になっています。
7.2.9 NCHARデータを使用したUTL_FILEパッケージの使用
UTL_FILE
パッケージは、NVARCHAR2
データ型のUnicode各国語文字セットのデータを処理します。NCHAR
およびNCLOB
は、暗黙的な変換でサポートされています。次の関数とプロシージャがあります。
-
FOPEN_NCHAR
この関数は、最大行サイズを指定して、入力用または出力用のファイルを各国語文字セット・モードでオープンします。
NVARCHAR2
のバッファの内容が(データベースの各国語文字セットによっては)AL16UTF16またはUTF8の可能性がある場合でも、ファイルの内容は常にUTF8で読取りおよび書込みされます。詳細は、「Oracle DatabaseでのUnicode規格のサポート」を参照してください。UTL_FILE
は、必要に応じてUTF8とAL16UTF16の間で変換を行います。 -
GET_LINE_NCHAR
このプロシージャは、ファイル・ハンドルが示すオープン・ファイルからテキストを1行読み込んで、出力バッファ・パラメータに配置します。ファイルは、各国語文字セット・モードでオープンされ、UTF8文字セットにエンコードされます。必要なバッファ・データ型は、
NVARCHAR2
です。NCHAR
、NCLOB
またはVARCHAR2
など、その他のデータ型の変数が指定された場合、テキストの読取りの後で、PL/SQLでNVARCHAR2
からの標準の暗黙的な変換が実行されます。 -
PUT_NCHAR
このプロシージャは、ファイル・ハンドルが示すオープン・ファイルに、バッファ・パラメータ内に格納されているテキスト文字列を書き込みます。このファイルは、各国語文字セット・モードでオープンされる必要があります。テキスト文字列は、UTF8文字セットで書き込まれます。必要なバッファ・データ型は、
NVARCHAR2
です。その他のデータ型の変数が指定された場合、テキストの書込みの前に、PL/SQLでNVARCHAR2
への暗黙的な変換が実行されます。 -
PUT_LINE_NCHAR
このプロシージャは、行セパレータが書き込まれたテキストに追加される以外は、
PUT_NCHAR
と同じです。 -
PUTF_NCHAR
このプロシージャは、
PUT_NCHAR
プロシージャの書式設定されたバージョンです。このプロシージャは、書式要素\nおよび%sを含む書式文字列を受け入れ、書式文字列内の%sの連続インスタンスを最大で5つの引数に置き換えることができます。書式文字列および引数に想定されるデータ・タイプは、NVARCHAR2
です。別のデータ型の変数が指定されていると、テキストの書式設定前に、PL/SQLでNVARCHAR2
への暗黙的な変換が実行されます。書式化されたテキストは、ファイル・ハンドルが示すファイルにUTF8文字セットで書き込まれます。このファイルは、各国語文字セット・モードでオープンされる必要があります。
前述の関数およびプロシージャでは、UTF8文字セット、つまりUnicode CESU-8エンコーディングでエンコードされたテキスト・ファイルを処理します。CESU-8の詳細は、「ユニバーサル文字セット」を参照してください。関数およびプロシージャは、必要に応じてUTF8とデータベースの各国語文字セット(UTF8またはAL16UTF16)間の変換を行います。
関連項目:
UTL_FILE
パッケージの詳細は、『Oracle Database PL/SQLパッケージおよびタイプ・リファレンス』を参照してください。
7.3 Unicodeを使用したOCIプログラミング
OCIはデータベースにアクセスするための最下位レベルのAPIであるため、可能な最大のパフォーマンスを提供します。OCIでUnicodeを使用するときは、次の内容を考慮してください。
7.3.1 Unicodeプログラミング用のOCIEnvNlsCreate()関数
OCIEnvNlsCreate()
関数を使用して、OCI環境の作成時にSQL CHAR
の文字セットとSQL NCHAR
の文字セットを指定します。これはOCIEnvCreate()
関数の拡張バージョンであり、2つの文字セットIDを指定できるように拡張された引数を取ります。OCI_UTF16ID UTF-16文字セットIDによって、Oracle9iリリース1(9.0.1)で導入されたUnicodeモードが置き換えられています。たとえば、次のようにします。
OCIEnv *envhp; status = OCIEnvNlsCreate((OCIEnv **)&envhp, (ub4)0, (void *)0, (void *(*) ()) 0, (void *(*) ()) 0, (void(*) ()) 0, (size_t) 0, (void **)0, (ub2)OCI_UTF16ID, /* Metadata and SQL CHAR character set */ (ub2)OCI_UTF16ID /* SQL NCHAR character set */);
Unicodeモードでは、OCIEnvCreate()
関数とともにOCI_UTF16フラグが使用されますが、このモードは非推奨です。
SQL CHAR
の文字セットとSQL NCHAR
の文字セットの両方にOCI_UTF16IDを指定すると、すべてのメタデータのバインドされた定義済データは、UTF-16でエンコードされます。メタデータには、SQL文、ユーザー名、エラー・メッセージおよび列名が含まれます。そのため、継承される操作はいずれもNLS_LANG
の設定に依存せず、すべてのメタテキスト・データ・パラメータ(text*
)は、UTF-16エンコーディングではUnicodeテキスト・データ型(utext*
)とみなされます。
OCI_UTF16ID文字セットIDを使用してOCIEnv()
関数を初期化するときにSQL文を準備するには、(utext*)
文字列を指定してOCIStmtPrepare()
関数をコールします。次の例は、Windowsプラットフォームでのみ動作します。他のプラットフォームの場合は、wchar_t
データ型を変更する必要があります。
const wchar_t sqlstr[] = L"SELECT * FROM ENAME=:ename"; ... OCIStmt* stmthp; sts = OCIHandleAlloc(envh, (void **)&stmthp, OCI_HTYPE_STMT, 0, NULL); status = OCIStmtPrepare(stmthp, errhp,(const text*)sqlstr, wcslen(sqlstr), OCI_NTV_SYNTAX, OCI_DEFAULT);
OCIEnv()
関数はすでにUTF-16の文字セットIDを使用して初期化されているため、データをバインドして定義するためにOCI_ATTR_CHARSET_ID
属性を設定する必要はありません。バインド変数名もUTF-16文字列であることが必要です。
/* Inserting Unicode data */ OCIBindByName(stmthp1, &bnd1p, errhp, (const text*)L":ename", (sb4)wcslen(L":ename"), (void *) ename, sizeof(ename), SQLT_STR, (void *)&insname_ind, (ub2 *) 0, (ub2 *) 0, (ub4) 0, (ub4 *)0, OCI_DEFAULT); OCIAttrSet((void *) bnd1p, (ub4) OCI_HTYPE_BIND, (void *) &ename_col_len, (ub4) 0, (ub4)OCI_ATTR_MAXDATA_SIZE, errhp); ... /* Retrieving Unicode data */ OCIDefineByPos (stmthp2, &dfn1p, errhp, (ub4)1, (void *)ename, (sb4)sizeof(ename), SQLT_STR, (void *)0, (ub2 *)0, (ub2*)0, (ub4)OCI_DEFAULT);
この操作はOCIExecute()
関数で実行されます。
関連項目:
7.3.2 OCI Unicodeのコード変換
クライアントとサーバーの文字セットが異なる場合は、OCIクライアントとデータベース・サーバーの間でUnicode文字セット変換が行われます。この変換は、環境によってクライアントまたはサーバーのいずれの側でも行われますが、通常はクライアント側で行われます。
7.3.2.1 データ整合性
OCI APIを適切にコールしないと、変換時にデータが消失する可能性があります。サーバーとクライアントの文字セットが異なる場合は、変換先文字セットが変換元文字セットより小さいセットであると、データが消失する可能性があります。両方の文字セットがUnicode文字セット(UTF8やAL16UTF16など)の場合、この潜在的な問題は回避できます。
SQL NCHAR
データ型をバインドまたは定義するときは、OCI_ATTR_CHARSET_FORM
属性をSQLCS_NCHAR
に設定する必要があります。この設定を行わないと、データはデータベース文字セットに変換されてから、各国語文字セットとの間で変換されるため、データが消失する可能性があります。データが消失するのは、データベース文字セットがUnicodeでない場合のみです。
7.3.2.2 Unicode使用時のOCIパフォーマンスに与える影響
冗長なデータ変換は、OCIアプリケーションのパフォーマンス低下の原因となる場合があります。このような冗長な変換は、次の2つの場合に発生します。
-
SQL
CHAR
データ型をバインドまたは定義して、OCI_ATTR_CHARSET_FORM
属性をSQLCS_NCHAR
に設定すると、クライアントの文字セットから各国語文字セットへのデータ変換と、各国語文字セットからデータベース文字セットへのデータ変換が行われます。データ消失はないと思われますが、必要な変換が1回の場合でも変換が2回行われます。 -
SQL
NCHAR
データ型をバインドまたは定義して、OCI_ATTR_CHARSET_FORM
を設定しないと、クライアントの文字セットからデータベース文字セットへの変換と、データベース文字セットから各国語データベース文字セットへのデータ変換が行われます。最悪の場合、データベース文字セットがクライアントの文字セットより小さいと、データが消失する可能性があります。
パフォーマンス上の問題を回避するために、ターゲット列のデータ型に基づいてOCI_ATTR_CHARSET_FORM
を常に適切に設定する必要があります。ターゲットのデータ型が不明の場合に、バインドおよび定義を行うときは、OCI_ATTR_CHARSET_FORM
属性をSQLCS_NCHAR
に設定してください。
次の表に、OCIの文字セットの変換を示します。
表7-3 OCIの文字セットの変換
OCIクライアント・バッファのデータ型 | OCI_ATTR_CHARSET_FORM | データベース内のターゲット列のデータ型 | 変換元と変換先 | コメント |
---|---|---|---|---|
|
|
|
OCIでの、UTF-16とデータベース文字セット |
予期しないデータ消失はありません。 |
|
|
|
OCIでの、UTF-16と各国語文字セット |
予期しないデータ消失はありません。 |
|
|
|
OCIでの、UTF-16と各国語文字セット データベース・サーバーでの、各国語文字セットとデータベース文字セット |
予期しないデータ消失はありませんが、各国語文字セットを経由して変換が行われるため、パフォーマンスが低下する可能性があります。 |
|
|
|
OCIでの、UTF-16とデータベース文字セット データベース・サーバーでの、データベース文字セットと各国語文字セット |
データベース文字セットがUnicodeでない場合は、データが消失する可能性があります。 |
|
|
|
OCIでの、 |
予期しないデータ消失はありません。 |
|
|
|
OCIでの、 |
予期しないデータ消失はありません。 |
|
|
|
OCIでの、 データベース・サーバーでの、各国語文字セットとデータベース文字セット |
予期しないデータ消失はありませんが、各国語文字セットを経由して変換が行われるため、パフォーマンスが低下する可能性があります。 |
|
|
|
OCIでの、 データベース・サーバーでの、データベース文字セットと各国語文字セット |
データベース文字セットを経由して変換が行われるため、データが消失する可能性があります。 |
7.3.2.3 OCI Unicodeのデータ拡張
データ変換によってデータが拡張し、バッファがオーバーフローする原因となる場合があります。バインド操作では、OCI_ATTR_MAXDATA_SIZE
属性をサーバー上に拡張後のデータを格納するのに十分なサイズに設定する必要があります。この設定が困難な場合は、表スキーマの変更を考慮する必要があります。定義操作では、拡張後のデータに対して十分なバッファ領域をクライアント・アプリケーションで割り当てる必要があります。バッファのサイズは、拡張後のデータの最大長に設定してください。次の計算で最大バッファ長を見積もることができます。
-
列データのバイト・サイズを取得します。
-
取得したサイズに、クライアント文字セットの1文字当たりの最大バイト数を乗算します。
この方法は最も簡単で速い方法ですが、正確ではないためメモリーを浪費する可能性があります。この方法は、文字セットのすべての組合せに適用できます。たとえば、UTF-16データのバインドと定義の場合は、次の例でクライアント・バッファを計算します。
ub2 csid = OCI_UTF16ID; oratext *selstmt = "SELECT ename FROM emp"; counter = 1; ... OCIStmtPrepare(stmthp, errhp, selstmt, (ub4)strlen((char*)selstmt), OCI_NTV_SYNTAX, OCI_DEFAULT); OCIStmtExecute ( svchp, stmthp, errhp, (ub4)0, (ub4)0, (CONST OCISnapshot*)0, (OCISnapshot*)0, OCI_DESCRIBE_ONLY); OCIParamGet(stmthp, OCI_HTYPE_STMT, errhp, &myparam, (ub4)counter); OCIAttrGet((void*)myparam, (ub4)OCI_DTYPE_PARAM, (void*)&col_width, (ub4*)0, (ub4)OCI_ATTR_DATA_SIZE, errhp); ... maxenamelen = (col_width + 1) * sizeof(utext); cbuf = (utext*)malloc(maxenamelen); ... OCIDefineByPos(stmthp, &dfnp, errhp, (ub4)1, (void *)cbuf, (sb4)maxenamelen, SQLT_STR, (void *)0, (ub2 *)0, (ub2*)0, (ub4)OCI_DEFAULT); OCIAttrSet((void *) dfnp, (ub4) OCI_HTYPE_DEFINE, (void *) &csid, (ub4) 0, (ub4)OCI_ATTR_CHARSET_ID, errhp); OCIStmtFetch(stmthp, errhp, 1, OCI_FETCH_NEXT, OCI_DEFAULT); ...
7.3.4 OCIでのSQL CHARデータ型のバインドと定義
SQL CHAR
データ型を使用してデータをバインドおよび定義するためのUnicode文字セットを指定するには、OCIBind()または
OCIDefine()
API後に、OCIAttrSet()
関数をコールして、適切な文字セットIDを設定する必要があります。次に一般的な2つの例を示します。
-
OCIBind()
またはOCIDefine()
の後にOCIAttrSet()
をコールして、UTF-16 Unicode文字セット・エンコーディングを指定します。たとえば、次のようにします。... ub2 csid = OCI_UTF16ID; utext ename[100]; /* enough buffer for ENAME */ ... /* Inserting Unicode data */ OCIBindByName(stmthp1, &bnd1p, errhp, (oratext*)":ENAME", (sb4)strlen((char *)":ENAME"), (void *) ename, sizeof(ename), SQLT_STR, (void *)&insname_ind, (ub2 *) 0, (ub2 *) 0, (ub4) 0, (ub4 *)0, OCI_DEFAULT); OCIAttrSet((void *) bnd1p, (ub4) OCI_HTYPE_BIND, (void *) &csid, (ub4) 0, (ub4)OCI_ATTR_CHARSET_ID, errhp); OCIAttrSet((void *) bnd1p, (ub4) OCI_HTYPE_BIND, (void *) &ename_col_len, (ub4) 0, (ub4)OCI_ATTR_MAXDATA_SIZE, errhp); ... /* Retrieving Unicode data */ OCIDefineByPos (stmthp2, &dfn1p, errhp, (ub4)1, (void *)ename, (sb4)sizeof(ename), SQLT_STR, (void *)0, (ub2 *)0, (ub2*)0, (ub4)OCI_DEFAULT); OCIAttrSet((void *) dfn1p, (ub4) OCI_HTYPE_DEFINE, (void *) &csid, (ub4) 0, (ub4)OCI_ATTR_CHARSET_ID, errhp); ...
バインドされたバッファが
utext
データ型の場合は、OCIBind()
またはOCIDefine()
のコール時にキャスト(text
*)を追加する必要があります。OCI_ATTR_MAXDATA_SIZE
属性の値は通常、サーバーの文字セットの列サイズによって決まります。これは、このサイズが、バインド操作の際に変換用の一時的なバッファ領域をサーバーに割り当てるためにのみ使用されているためです。 -
NLS_LANG
文字セットをUTF8またはAL32UTF8として指定して、OCIBind()
またはOCIDefine()
をコールします。環境変数
NLS_LANG
にUTF8またはAL32UTF8を設定できます。Unicodeを使用していない場合と同様に、OCIBind()
およびOCIDefine()
をコールします。環境変数NLS_LANG
をUTF8またはAL32UTF8に設定し、次のOCIプログラムを実行します。... oratext ename[100]; /* enough buffer size for ENAME */ ... /* Inserting Unicode data */ OCIBindByName(stmthp1, &bnd1p, errhp, (oratext*)":ENAME", (sb4)strlen((char *)":ENAME"), (void *) ename, sizeof(ename), SQLT_STR, (void *)&insname_ind, (ub2 *) 0, (ub2 *) 0, (ub4) 0, (ub4 *)0, OCI_DEFAULT); OCIAttrSet((void *) bnd1p, (ub4) OCI_HTYPE_BIND, (void *) &ename_col_len, (ub4) 0, (ub4)OCI_ATTR_MAXDATA_SIZE, errhp); ... /* Retrieving Unicode data */ OCIDefineByPos (stmthp2, &dfn1p, errhp, (ub4)1, (void *)ename, (sb4)sizeof(ename), SQLT_STR, (void *)0, (ub2 *)0, (ub2*)0, (ub4)OCI_DEFAULT); ...
7.3.5 OCIでのSQL NCHARデータ型のバインドと定義
OCI使用時は、UTF-16のバインドまたは定義を使用してSQL NCHAR
データ型にアクセスすることをお薦めします。Oracle9i から、SQL NCHAR
データ型は、UTF8またはAL16UTF16のいずれかのエンコーディングを使用したUnicodeデータ型になっています。SQL NCHAR
データ型のデータにアクセスするには、データ消失が発生しない適切なデータ変換が実行されるように、バインドまたは定義してから実行するまでにOCI_ATTR_CHARSET_FORM
属性をSQLCS_NCHAR
に設定します。SQL NCHAR
データ型のデータ長は常に、Unicodeコード単位の数で表されます。
次のプログラムは、NCHAR
データ列に対するデータの挿入とフェッチの一般的な例です。
... ub2 csid = OCI_UTF16ID; ub1 cform = SQLCS_NCHAR; utext ename[100]; /* enough buffer for ENAME */ ... /* Inserting Unicode data */ OCIBindByName(stmthp1, &bnd1p, errhp, (oratext*)":ENAME", (sb4)strlen((char *)":ENAME"), (void *) ename, sizeof(ename), SQLT_STR, (void *)&insname_ind, (ub2 *) 0, (ub2 *) 0, (ub4) 0, (ub4 *)0, OCI_DEFAULT); OCIAttrSet((void *) bnd1p, (ub4) OCI_HTYPE_BIND, (void *) &cform, (ub4) 0, (ub4)OCI_ATTR_CHARSET_FORM, errhp); OCIAttrSet((void *) bnd1p, (ub4) OCI_HTYPE_BIND, (void *) &csid, (ub4) 0, (ub4)OCI_ATTR_CHARSET_ID, errhp); OCIAttrSet((void *) bnd1p, (ub4) OCI_HTYPE_BIND, (void *) &ename_col_len, (ub4) 0, (ub4)OCI_ATTR_MAXDATA_SIZE, errhp); ... /* Retrieving Unicode data */ OCIDefineByPos (stmthp2, &dfn1p, errhp, (ub4)1, (void *)ename, (sb4)sizeof(ename), SQLT_STR, (void *)0, (ub2 *)0, (ub2*)0, (ub4)OCI_DEFAULT); OCIAttrSet((void *) dfn1p, (ub4) OCI_HTYPE_DEFINE, (void *) &csid, (ub4) 0, (ub4)OCI_ATTR_CHARSET_ID, errhp); OCIAttrSet((void *) dfn1p, (ub4) OCI_HTYPE_DEFINE, (void *) &cform, (ub4) 0, (ub4)OCI_ATTR_CHARSET_FORM, errhp); ...
7.3.6 OCIでのSQL NCHAR文字列リテラルの処理
デフォルトでは、NCHAR
のリテラル置換はOCIでは有効になっていません。OCIでこれを有効にするには、環境変数ORA_NCHAR_LITERAL_REPLACE
をTRUE
に設定します。
また、OCIEnvCreate()
およびOCIEnvNlsCreate()
でOCI_NCHAR_LITERAL_REPLACE_ON
モードとOCI_NCHAR_LITERAL_REPLACE_OFF
モードを使用して、プログラムでリテラル置換をOCIで有効にすることもできます。たとえば、OCIEnvCreate(OCI_NCHAR_LITERAL_REPLACE_ON)
と記述するとNCHAR
のリテラル置換が有効になり、OCIEnvCreate(OCI_NCHAR_LITERAL_REPLACE_OFF)
と記述すると置換が無効になります。
たとえば、次の文があるとします。
int main(argc, argv) { OCIEnv *envhp; if (OCIEnvCreate((OCIEnv **) &envhp, (ub4)OCI_THREADED|OCI_NCHAR_LITERAL_REPLACE_ON, (dvoid *)0, (dvoid * (*)(dvoid *, size_t)) 0, (dvoid * (*)(dvoid *, dvoid *, size_t))0, (void (*)(dvoid *, dvoid *)) 0, (size_t) 0, (dvoid **) 0)) { printf("FAILED: OCIEnvCreate()\n"; return 1; } ... }
ノート:
NCHAR
のリテラル置換が有効な場合、OCIStmtPrepare
およびOCIStmtPrepare2
は、SQLテキスト内のN'
リテラルをU'
リテラルに変換し、結果のSQLテキストを文ハンドルに格納します。この結果、アプリケーションがOCI_ATTR_STATEMENT
を使用してOCI文ハンドルからSQLテキストを取り出す場合、SQLテキストは元のテキストで指定されたN'
ではなくU'
を戻します。
関連項目:
-
環境変数の設定方法の詳細は、『Oracle Database管理者ガイド』を参照してください
7.3.7 OCIでのCLOB UnicodeデータおよびNCLOB Unicodeデータのバインドと定義
CLOB
またはNCLOB
の列に対してUTF-16データの書込み(バインド)および読込み(定義)を行うには、UTF-16文字セットIDをOCILobWrite()
およびOCILobRead()
として指定する必要があります。UTF-16データをCLOB
列に書き込むときは、OCILobWrite()
を次のようにコールしてください。
... ub2 csid = OCI_UTF16ID; err = OCILobWrite (ctx->svchp, ctx->errhp, lobp, &amtp, offset, (void *) buf, (ub4) BUFSIZE, OCI_ONE_PIECE, (void *)0, (sb4 (*)()) 0, (ub2) csid, (ub1) SQLCS_IMPLICIT);
amtp
パラメータは、Unicodeコード単位でのデータ長です。offset
パラメータは、データ列の開始からのデータのオフセットを示しています。csid
パラメータは、UTF-16データ用に設定する必要があります。
CLOB
列からUTF-16データを読み込むには、OCILobRead()
を次のようにコールします。
... ub2 csid = OCI_UTF16ID; err = OCILobRead(ctx->svchp, ctx->errhp, lobp, &amtp, offset, (void *) buf, (ub4)BUFSIZE , (void *) 0, (sb4 (*)()) 0, (ub2)csid, (ub1) SQLCS_IMPLICIT);
データ長は常に、Unicodeコード単位の数で表されます。エンコーディングがUTF-16であるため、1つのUnicode補助文字は2コード単位としてカウントされます。LOB
列のバインドまたは定義後、OCILobGetLength()
を使用してLOB
列に格納されているデータ長を測定できます。UTF-16としてデータをバインドまたは定義している場合、戻り値はコード単位によるデータ長です。
err = OCILobGetLength(ctx->svchp, ctx->errhp, lobp, &lenp);
NCLOB
を使用している場合は、OCI_ATTR_CHARSET_FORM
をSQLCS_NCHAR
に設定する必要があります。
7.4 Unicodeを使用したPro*C/C++プログラミング
Pro*C/C++には、データベースとの間でUnicodeデータの挿入や取出しを行う方法が3通り用意されています。
-
Pro*C/C++の
VARCHAR
データ型またはネイティブなC/C++のtext
データ型を使用すると、UTF8またはAL32UTF8データベースのSQLCHAR
データ型で格納されているUnicodeデータにプログラムでアクセスできます。C/C++ のネイティブなtext
型をプログラムで使用することもできます。 -
Pro*C/C++の
UVARCHAR
データ型またはネイティブなC/C++のutext
データ型を使用すると、データベースのNCHAR
データ型で格納されているUnicodeデータにプログラムでアクセスできます。 -
Pro*C/C++ の
NVARCHAR
データ型を使用すると、NCHAR
データ型で格納されているUnicodeデータにプログラムでアクセスできます。Pro*C/C++プログラムでのUVARCHAR
とNVARCHAR
の相違は、UVARCHAR
データ型のデータはutext
バッファに格納されているのに対して、NVARCHAR
データ型のデータはtext
データ型で格納されている点です。
Pro*C/C++では、SQLテキストに対してUnicode OCI APIを使用しません。そのため、埋込みSQLテキストは、環境変数NLS_LANG
に指定されている文字セットでエンコードする必要があります。
この項では、次の項目について説明します。
7.4.1 UnicodeでのPro*C/C++データ変換
データ変換はOCIレイヤーで行われますが、Pro*C/C++プログラムで使用されているデータ型に基づいて、どの変換手順を使用するかをOCIに対して指示するのは、Pro*C/C++プリプロセッサです。次の表に、変換手順を示します。
表7-4 Pro*C/C++のバインドと定義でのデータ変換
Pro*C/C++データ型 | SQLデータ型 | 変換手順 |
---|---|---|
|
|
OCIで発生する、 |
|
|
OCIで発生する、 データベース・サーバーで発生する、データベース文字セットと各国語文字セットの間の変換 |
|
|
OCIで発生する、 |
|
|
OCIで発生する、 データベース・サーバーでの、各国語文字セットとデータベース文字セットの間の変換 |
|
|
OCIで発生する、UTF-16と各国語文字セットの間の変換 |
|
|
OCIで発生する、UTF-16と各国語文字セットの間の変換 データベース・サーバーで発生する、各国語文字セットからデータベース文字セットへの変換 |
7.4.2 Pro*C/C++でのVARCHARデータ型の使用
Pro*C/C++のVARCHAR
データ型は、lengthフィールドとtextバッファ・フィールド付きの構造体に事前処理されます。次の例では、C/C++のシステム固有のデータ型
text
とPro*C/C++のVARCHAR
データ型を使用して、表の列をバインドおよび定義します。
#include <sqlca.h> main() { ... /* Change to STRING datatype: */ EXEC ORACLE OPTION (CHAR_MAP=STRING) ; text ename[20] ; /* unsigned short type */ varchar address[50] ; /* Pro*C/C++ varchar type */ EXEC SQL SELECT ename, address INTO :ename, :address FROM emp; /* ename is NULL-terminated */ printf(L"ENAME = %s, ADDRESS = %.*s\n", ename, address.len, address.arr); ... }
Pro*C/C++プログラムでVARCHAR
データ型またはシステム固有のtext
データ型を使用すると、プリプロセッサでは、そのプログラムがデータベースのSQL NCHAR
データ型ではなく、SQL CHAR
データ型の列にアクセスしているとみなします。次に、プリプロセッサは、OCI_ATTR_CHARSET_FORM
属性に対するSQLCS_IMPLICIT
値を使用してバインドまたは定義することで、この動きを反映するためのC/C++コードを生成します。その結果、バインド変数または定義変数がデータベースのSQL NCHAR
データ型の列にバインドされると、データベース・サーバーでの暗黙的な変換によって、データベース文字セットと各国語データベース文字セットの間でデータの変換が行われます。データベース文字セットが各国語文字セットより小さい場合は、変換時にデータが消失します。
7.4.3 Pro*C/C++でのNVARCHARデータ型の使用
Pro*C/C++のNVARCHAR
データ型は、Pro*C/C++のVARCHAR
データ型と類似しています。データベースのSQL NCHAR
データ型にアクセスするために使用する必要があります。このデータ型は、SQL NCHAR
データ型の列にテキスト・バッファをバインドまたは定義するようにPro*C/C++プリプロセッサに通知します。プリプロセッサは、バインド変数または定義変数のOCI_ATTR_CHARSET_FORM
属性にSQLCS_NCHAR
値を指定します。この結果、データベースで暗黙的な変換は行われません。
SQL CHAR
データ型の列に対してNVARCHAR
バッファがバインドされると、(NLS_LANG
文字セットでエンコードされている)NVARCHAR
バッファのデータは、OCIで各国語文字セットとの変換が行われ、次に、データベース・サーバーでデータベース文字セットに変換されます。NLS_LANG
文字セットがデータベース文字セットより大きい場合は、データが消失する可能性があります。
7.4.4 Pro*C/C++でのUVARCHARデータ型の使用
UVARCHAR
データ型は、length
フィールドとutext
バッファ・フィールド付きの構造体に事前処理されます。次のコード例には、2つのホスト変数ename
とaddress
が含まれています。ename
ホスト変数は、Unicode文字を20文字格納するutext
バッファとして宣言されています。address
ホスト変数は、Unicode文字を50文字格納するuvarchar
バッファとして宣言されています。len
フィールドとarr
フィールドは、構造体のフィールドとしてアクセス可能です。
#include <sqlca.h> #include <sqlucs2.h> main() { ... /* Change to STRING datatype */ EXEC ORACLE OPTION (CHAR_MAP=STRING); utext ename[20]; /* unsigned short type */ uvarchar address[50]; /* Pro*C/C++ uvarchar type */ EXEC SQL SELECT ename, address INTO :ename, :address FROM emp; /* ename is NULL-terminated */ wprintf(L"ENAME = %s, ADDRESS = %.*s\n", ename, address.len, address.arr); ... }
Pro*C/C++プログラムでUVARCHAR
データ型またはシステム固有のutext
データ型を使用すると、プリプロセッサでは、プログラムがSQL NCHAR
データ型にアクセスしているとみなします。次に、プリプロセッサは、OCI_ATTR_CHARSET_FORM
属性に対するSQLCS_NCHAR
値を使用してバインドまたは定義することで、C/C++コードを生成します。この結果、バインド変数または定義変数がSQL NCHAR
データ型の列にバインドされると、データベース・サーバーでは各国語文字セットからのデータの暗黙的な変換が行われます。ただし、各国語文字セットは常にデータベース文字セットより大きいため、この状況でデータが消失することはありません。
7.5 Unicodeを使用したJDBCプログラミング
Oracleには、Oracle Databaseの文字データにJavaプログラムでアクセスするために次のJDBCドライバが用意されています。
-
JDBC OCIドライバ
-
JDBC Thinドライバ
-
サーバー側JDBC内部ドライバ
-
サーバー側JDBC Thinドライバ
Javaプログラムでは、SQL CHAR
データ型およびNCHAR
データ型の列との間で文字データの挿入や取出しを行うことができます。特に、JDBCを使用すると、JavaプログラムでJava文字列をSQL CHAR
データ型およびNCHAR
データ型にバインドしたり定義できます。Javaのstring
データ型はUTF-16でエンコードされているため、データベースとの間で取り出したり挿入するデータは、UTF-16とデータベース文字セットまたは各国語文字セット間で変換する必要があります。また、JDBCを使用すると、PL/SQL文とSQL文をJava文字列に指定できるため、非ASCIIスキーマ・オブジェクト名と文字列リテラルを参照できます。
データベースの接続時、JDBCは、サーバーのNLS_LANGUAGE
およびNLS_TERRITORY
パラメータを、JDBCドライバを実行するJava VMのロケールに対応する値に設定します。この操作により、サーバーとJavaクライアントが確実に同じ言語で通信できるようになります。その結果、サーバーから戻されるOracleエラー・メッセージは、クライアントのロケールと同じ言語になります。
この項では、次の項目について説明します。
7.5.1 SQL CHARデータ型へのJava文字列のバインドと定義
Oracle JDBCドライバを使用すると、Java文字列のバインド変数または定義変数を使用して、データベースのSQL CHAR
データ型にアクセスできます。次のコードは、Java文字列をCHAR
列にバインドする方法を示しています。
int employee_id = 12345; String last_name = "Joe"; PreparedStatement pstmt = conn.prepareStatement("INSERT INTO" + "employees (last_name, employee_id) VALUES (?, ?)"); pstmt.setString(1, last_name); pstmt.setInt(2, employee_id); pstmt.execute(); /* execute to insert into first row */ employee_id += 1; /* next employee number */ last_name = "\uFF2A\uFF4F\uFF45"; /* Unicode characters in name */ pstmt.setString(1, last_name); pstmt.setInt(2, employee_id); pstmt.execute(); /* execute to insert into second row */
ターゲットSQL列は、そのデータ型と長さを指定することで定義できます。データ型と長さを指定してSQL CHAR
列を定義すると、JDBCではこの情報を使用してSQL CHAR
列からデータをフェッチする操作のパフォーマンスが最適化されます。次に、SQL CHAR
列の定義例を示します。
OraclePreparedStatement pstmt = (OraclePreparedStatement) conn.prepareStatement("SELECT ename, empno from emp"); pstmt.defineColumnType(1,Types.VARCHAR, 3); pstmt.defineColumnType(2,Types.INTEGER); ResultSet rest = pstmt.executeQuery(); String name = rset.getString(1); int id = reset.getInt(2);
PreparedStatement
をOraclePreparedStatement
にキャストして、defineColumnType()
をコールする必要があります。defineColumnType()
の2番目のパラメータは、ターゲットSQL列のデータ型です。3番目のパラメータは、文字数による長さです。
7.5.2 SQL NCHARデータ型へのJava文字列のバインドと定義
Java文字列の変数をSQL NCHAR
データ型にバインドまたは定義するために、OracleにはsetFormOfUse()
メソッドを含む拡張PreparedStatement
が用意されています。このメソッドを使用して、バインド変数のターゲット列がSQL NCHAR
データ型であることを明示的に指定できます。次のコードは、Java文字列をNCHAR
列にバインドする方法を示しています。
int employee_id = 12345; String last_name = "Joe" oracle.jdbc.OraclePreparedStatement pstmt = (oracle.jdbc.OraclePreparedStatement) conn.prepareStatement("INSERT INTO employees (last_name, employee_id) VALUES (?, ?)"); pstmt.setFormOfUse(1, oracle.jdbc.OraclePreparedStatement.FORM_NCHAR); pstmt.setString(1, last_name); pstmt.setInt(2, employee_id); pstmt.execute(); /* execute to insert into first row */ employee_id += 1; /* next employee number */ last_name = "\uFF2A\uFF4F\uFF45"; /* Unicode characters in name */ pstmt.setString(1, last_name); pstmt.setInt(2, employee_id); pstmt.execute(); /* execute to insert into second row */
ターゲットのSQL NCHAR
列は、そのデータ型、使用フォームおよび長さを指定することで定義できます。JDBCでは、この情報を使用して、これらの列からSQL NCHAR
データをフェッチする操作のパフォーマンスが最適化されます。次に、SQL NCHAR
列の定義例を示します。
OraclePreparedStatement pstmt = (OraclePreparedStatement) conn.prepareStatement("SELECT ename, empno from emp"); pstmt.defineColumnType(1,Types.VARCHAR, 3, OraclePreparedStatement.FORM_NCHAR); pstmt.defineColumnType(2,Types.INTEGER); ResultSet rest = pstmt.executeQuery(); String name = rset.getString(1); int id = reset.getInt(2);
SQL NCHAR
列を定義するには、defineColumnType()
の最初の引数にSQL CHAR
列と等価のデータ型を指定し、第2の引数に文字数による長さを指定し、第4の引数に使用フォームを指定する必要があります。
使用フォームの引数を明示的に指定しなくても、NCHAR
列にJava文字列をバインドまたは定義できます。その場合は、次のことに注意してください。
-
setString()
メソッドに引数を指定しないと、JDBCはバインド変数または定義変数がSQLCHAR
列用であるとみなします。その結果、JDBCはこれらの変数をデータベース文字セットに変換しようとします。データがデータベースに到着すると、そのデータベースはデータベース文字セットのデータを各国語文字セットに暗黙的に変換します。データベース文字セットが各国語文字セットのサブセットの場合は、この変換時にデータが消失する可能性があります。各国語文字セットはUTF8またはAL16UTF16であるため、データベース文字セットがUTF8またはAL32UTF8以外の場合、データは消失します。 -
SQL
CHAR
データ型からSQLNCHAR
データ型への暗黙的な変換はデータベースで行われるため、データベースのパフォーマンスが低下します。
また、SQL CHAR
データ型の列にJava文字列をバインドまたは定義しても、使用フォームの引数を指定すると、データベースのパフォーマンスが低下します。ただし、各国語文字セットは常にデータベース文字セットより大きいため、データが消失することはありません。
7.5.2.1 NCHARデータ型用の新しいJDBC4.0メソッド
JDBC 11.1では、新しいJDBC 4.0 (JDK6) SQLデータ型NCHAR
、NVARCHAR
、LONGNVARCHAR
およびNCLOB
に対するサポートが追加されています。各国語の文字値を取得するために、アプリケーションから次のいずれかのメソッドをコールできます。
-
getNString
-
getNClob
-
getNCharacterStream
getNClob
メソッドは、取り出した値が確かにNCLOB
かどうかを検証します。他の点では、これらのメソッドは文字N
のない対応するメソッドと同等です。
各国語キャラクタ・タイプのパラメータ・マーカーの値を指定するために、アプリケーションから次のいずれかのメソッドをコールできます。
-
setNString
-
setNCharacterStream
-
setNClob
これらのメソッドは、setFormOfUse(..., OraclePreparedStatement.FORM_NCHAR)
のコールが先行する文字N
のない対応するメソッドと同等です。
関連項目:
詳細は、『Oracle Database JDBC開発者ガイド』を参照してください。
7.5.3 コード変更なしのSQL NCHARデータ型の使用方法
Javaアプリケーションで使用フォームの引数をデフォルトで指定するかどうかを指示できるように、Oracle JDBCドライバにはJavaシステム・プロパティが導入されています。このプロパティの用途は次のとおりです。
-
SQL
CHAR
データ型にアクセスする既存のアプリケーションは、SQLNCHAR
データ型をサポートするように移行できます。これにより、コード行を変更せずに世界中にデプロイできるようになります。 -
アプリケーションでは、SQL
NCHAR
列をバインドおよび定義するときに、setFormOfUse()
メソッドをコールする必要はありません。アプリケーション・コードは、バックエンド・データベースで使用中のデータ型に依存せずにそのまま使用できます。このプロパティ・セットを使用すると、アプリケーションをSQLCHAR
またはSQLNCHAR
の使用から簡単に移行できます。
Javaシステム・プロパティは、Javaアプリケーションを起動するコマンドラインで指定します。このフラグを指定する構文は、次のとおりです。
java -Doracle.jdbc.defaultNChar=true <application class>
このプロパティを指定すると、Oracle JDBCドライバでは、アプリケーションのすべてのバインドおよび定義操作に使用フォームの引数が存在するものとみなされます。
データベース・スキーマがSQL CHAR
列とSQL NCHAR
列の両方で構成されている場合は、このフラグを使用すると、データベース・サーバーで暗黙的な変換が実行されるため、SQL CHAR
列へのアクセス時にパフォーマンスが低下することがあります。
関連項目:
暗黙的な変換によるパフォーマンス低下の詳細は、「JDBCドライバのデータ変換」を参照してください
7.5.4 JDBCでのSQL NCHAR文字列リテラルの使用
JDBCでNCHAR
文字列リテラルを使用する場合は、処理前に文字がデータベース文字セットに変換されるため、データ消失の可能性があります。詳細は、「NCHAR文字列リテラルの置換」を参照してください。
NCHAR
文字列リテラルを保持する処理を適切に実行するには、プロパティ・セットoracle.jdbc.convertNcharLiterals
を有効にします。値がtrueの場合、このオプションは有効化され、それ以外の場合は無効化されます。デフォルトでは「False」に設定されています。オプションを有効化する方法は2通りあり、a)Javaシステム・プロパティとしてまたはb)接続プロパティとして設定できます。有効化すると、VM(システム・プロパティ)または接続(接続プロパティ)のすべてのSQLで変換が実行されます。たとえば、次のように、Javaシステム・プロパティとしてプロパティを設定できます。
java -Doracle.jdbc.convertNcharLiterals="true" ...
または、次のように接続プロパティとして設定できます。
Properties props = new Properties(); ... props.setProperty("oracle.jdbc.convertNcharLiterals", "true"); Connection conn = DriverManager.getConnection(url, props);
接続プロパティとして設定した場合は、システム・プロパティの設定が上書きされます。
7.5.5 JDBCドライバのデータ変換
Java文字列は常にUTF-16でエンコードされているため、JDBCドライバは、データベース文字セットからUTF-16または各国語文字セットにデータを透過的に変換します。変換手順は、JDBCドライバごとにそれぞれ異なります。
7.5.5.1 OCIドライバ用のデータ変換
OCIドライバの場合、SQL文はデータベースに送信され処理される前に、ドライバによって常にデータベース文字セットに変換されます。データベース文字セットがUS7ASCIIでもWE8ISO8859P1でもない場合、ドライバはまずSQL文をJavaでUTF-8に変換し、その後Cでデータベース文字セットに変換します。それ以外の場合は、SQL文をデータベース文字セットに直接変換します。Java文字列のバインド変数については、次の表に様々な状況に対応する変換手順が要約されています。Java文字列の定義変数については、同じ変換手順が逆方向に行われます。
表7-5 OCIドライバの変換手順
使用フォーム | SQLデータ型 | 変換手順 |
---|---|---|
|
|
JDBCドライバで行われる、UTF-16エンコーディングのJava文字列とデータベース文字セットの間の変換。 |
|
|
JDBCドライバで行われる、UTF-16エンコーディングのJava文字列とデータベース文字セットの間の変換。さらに、データベース・サーバーで行われる、データベース文字セットと各国語文字セットの間の変換。 |
|
|
JDBCドライバで行われる、UTF-16エンコーディングのJava文字列と各国語文字セットの間の変換。 |
|
|
JDBCドライバで行われる、UTF-16エンコーディングのJava文字列と各国語文字セットの間の変換。さらに、データベース・サーバーで行われる、各国語文字セットとデータベース文字セットの間の変換。 |
7.5.5.2 Thinドライバ用のデータ変換
SQL文は、データベースに送信され処理される前に、ドライバによって常にデータベース文字セットまたはUTF-8のいずれかに変換されます。データベース文字セットが次のいずれかの文字セットの場合、ドライバはSQL文をデータベース文字セットに変換します。
-
US7ASCII
-
WE8ISO8859P1
-
WE8DEC
-
WE8MSWIN1252
それ以外の場合、ドライバはSQL文をUTF-8に変換し、データベースに対してSQL文を処理するにはさらに変換が必要であることを通知します。その場合、データベースはSQL文をデータベース文字セットに変換します。Java文字列のバインド変数の場合、Thinドライバに対しては、次の表に示されている変換手順が使用されます。Java文字列の定義変数については、同じ変換手順が逆方向に行われます。前述の4つの文字セットは、表では選択されたキャラクタ・セットと表示されています。
表7-6 Thinドライバの変換手順
使用フォーム | SQLデータ型 | データベース文字セット | 変換手順 |
---|---|---|---|
|
|
選択された文字セットの1つ |
thinドライバで行われる、UTF-16エンコーディングのJava文字列とデータベース文字セットの間の変換。 |
|
|
選択された文字セットの1つ |
thinドライバで行われる、UTF-16エンコーディングのJava文字列とデータベース文字セットの間の変換。さらに、データベース・サーバーで行われる、データベース文字セットと各国語文字セットの間の変換。 |
|
|
選択された文字セット以外 |
thinドライバで行われる、UTF-16エンコーディングのJava文字列とUTF-8の間の変換。さらに、データベース・サーバーで行われる、UTF-8とデータベース文字セットの間の変換。 |
|
|
選択された文字セット以外 |
thinドライバで行われる、UTF-16エンコーディングのJava文字列とUTF-8の間の変換。さらに、データベース・サーバーで行われる、UTF-8からデータベース文字セット、さらに各国語文字セットへの変換。 |
|
|
任意 |
thinドライバで行われる、UTF-16エンコーディングのJava文字列と各国語文字セットの間の変換。さらに、データベース・サーバーで行われる、各国語文字セットとデータベース文字セットの間の変換。 |
|
|
任意 |
thinドライバで行われる、UTF-16エンコーディングのJava文字列と各国語文字セットの間の変換。 |
7.5.6 Oracleオブジェクト型のoracle.sql.CHARの使用
JDBCドライバはOracleオブジェクト型をサポートしています。Oracleオブジェクトは、データベース文字セットまたは各国語文字セットで表現したオブジェクトとして、常にデータベースからクライアントに送信されます。つまり、「JDBCドライバのデータ変換」に示したデータ変換の流れは、Oracleオブジェクトのアクセスには適用されないことを意味します。かわりに、データベースからクライアントにオブジェクト型のSQL CHAR
およびSQL NCHAR
データを送信するために、oracle.sql.CHAR
クラスが使用されます。
この項には次のトピックが含まれます:
7.5.6.1 oracle.sql.CHAR
oracle.sql.CHAR
クラスには、文字データを変換するための特別な機能があります。Oracle文字セットは、oracle.sql.CHAR
クラスの主な属性です。oracle.sql.CHAR
オブジェクトの構成時には、常にOracle文字セットが渡されます。既知の文字セットがない場合、oracle.sql.CHAR
オブジェクトのデータのバイトには意味がありません。
oracle.sql.CHAR
クラスは、文字データを文字列に変換するために、次のメソッドを提供します。
-
getString()
oracle.sql.CHAR
オブジェクトが表現する一連の文字を文字列に変換し、Java文字列オブジェクトを戻します。文字セットが認識されない場合、getString()
はSQLException
を戻します。 -
toString()
getString()
に類似していますが、文字セットが認識されない場合、toString()
はoracle.sql.CHAR
データを16進表現で戻し、SQLException
は戻しません。 -
getStringWithReplacement()
getString()
に類似していますが、このoracle.sql.CHAR
オブジェクトの文字セットにUnicode表現のない文字は、デフォルトの置換文字で置き換えられます。デフォルトの置換文字は、文字セットによって異なりますが、通常は疑問符(?)です。
oracle.sql.CHAR
オブジェクトを(プリコンパイルされたSQL文に渡すなどの目的で)手動で構成できます。oracle.sql.CHAR
オブジェクトを構成する場合は、oracle.sql.CharacterSet
クラスのインスタンスを使用して、oracle.sql.CHAR
オブジェクトに文字セット情報を提供する必要があります。oracle.sql.CharacterSet
クラスの各インスタンスは、Oracleでサポートしている文字セットの1つを表します。
次のタスクを実行してoracle.sql.CHAR
オブジェクトを構成します。
-
静的
CharacterSet.make
メソッドをコールして、CharacterSet
インスタンスを作成します。これは、文字セットのクラスを作成するメソッドです。入力として有効なOracle文字セット(OracleId
)が必要です。たとえば、次のようにします。int OracleId = CharacterSet.JA16SJIS_CHARSET; // this is character set 832 ... CharacterSet mycharset = CharacterSet.make(OracleId);
Oracleでサポートされている各文字セットには、一意の事前定義済
OracleId
があります。OracleId
は、Oracle_character_set_name
_CHARSET
として指定した文字セットとしていつでも参照できます。Oracle_character_set_name
は、Oracle文字セットです。 -
oracle.sql.CHAR
オブジェクトを構成します。コンストラクタに、文字列(または文字列を表現するバイト)と、文字セットに基づくバイトの解析方法を示すCharacterSet
オブジェクトを渡します。たとえば、次のようにします。String mystring = "teststring"; ... oracle.sql.CHAR mychar = new oracle.sql.CHAR(teststring, mycharset);
oracle.sql.CHAR
クラスには複数のコンストラクタがあり、CharacterSet
オブジェクトとともに文字列、バイト配列またはオブジェクトを入力としてとることができます。文字列の場合は、oracle.sql.CHAR
オブジェクトに置かれる前に、CharacterSet
オブジェクトが示す文字セットに変換されます。
サーバー(データベース)とクライアント(またはクライアントで実行中のアプリケーション)は、異なる文字セットを使用できます。このクラスのメソッドを使用してサーバーとクライアントの間でデータを転送する場合、JDBCドライバはサーバー文字セットとクライアント文字セットの間でデータを変換する必要があります。
7.5.6.2 oracle.sql.CHARを使用したSQL CHAR属性とNCHAR属性へのアクセス
次に、SQLでオブジェクト型を作成する例を示します。
CREATE TYPE person_type AS OBJECT ( name VARCHAR2(30), address NVARCHAR2(256), age NUMBER); CREATE TABLE employees (id NUMBER, person PERSON_TYPE);
このオブジェクト型に対応するJavaクラスは、次のように構成できます。
public class person implement SqlData { oracle.sql.CHAR name; oracle.sql.CHAR address; oracle.sql.NUMBER age; // SqlData interfaces getSqlType() {...} writeSql(SqlOutput stream) {...} readSql(SqlInput stream, String sqltype) {...} }
ここでは、oracle.sql.CHAR
クラスは、Oracleオブジェクト型のNAME
属性(VARCHAR2
データ型)にマップするために使用されています。JDBCは、このクラスに、データベースのVARCHAR2
データのバイト表現およびデータベース文字セットに対応するCharacterSet
オブジェクトを導入します。次のコードでは、employees
表からperson
オブジェクトを取り出しています。
TypeMap map = ((OracleConnection)conn).getTypeMap(); map.put("PERSON_TYPE", Class.forName("person")); conn.setTypeMap(map); . . . . . . ResultSet rs = stmt.executeQuery("SELECT PERSON FROM EMPLOYEES"); rs.next(); person p = (person) rs.getObject(1); oracle.sql.CHAR sql_name = p.name; oracle.sql.CHAR sql_address=p.address; String java_name = sql_name.getString(); String java_address = sql_address.getString();
oracle.sql.CHAR
クラスのgetString()
メソッドは、OracleのJavaデータ変換クラスをコールしてJava文字列を戻すことで、データベース文字セットまたは各国語文字セットのバイト配列をUTF-16に変換します。rs.getObject(1)
コールが動作するように、SqlData
インタフェースをクラスperson
に実装し、オブジェクト型PERSON_TYPE
のJavaクラスへのマッピングを示すようにTypemap
map
を設定する必要があります。
7.5.7 JDBCを使用した場合のSQL CHARデータへのアクセスの制限
この項の内容は、次のとおりです。
7.5.7.1 マルチバイト・データベース環境での文字整合性の問題
Oracle JDBCドライバは、文字データがデータベースに挿入されるときまたはデータベースから取り出されるときに、適切に文字セット変換を実行します。このドライバは、Javaクライアントが使用するUnicode文字とOracle Database文字セットの間の変換を行います。Java Unicode文字セットとデータベース文字セットの間のラウンドトリップ変換で、Javaに戻された文字データは、情報の一部が消失している場合があります。これは、複数のUnicode文字が、データベース文字セットの1つの文字にマップされた場合に発生します。たとえば、Unicodeの全角のチルド付き文字(0xFF5E)が、OracleのJA16SJIS文字セットにマップされている場合があります。Unicode文字のラウンドトリップ変換で、このUnicode文字は0x301Cとなります。これは波形のダッシュ(日本語で、一般的に範囲を示す場合に使用されます)で、チルドではありません。
次の図に、チルド文字のラウンドトリップ変換を示します。
この問題は、OracleのJDBCの不具合ではありません。異なるオペレーティング・システムでの文字マッピング仕様が不確定であるために発生する問題です。この問題は、一部のOracle文字セット(JA16SJIS、JA16EUC、ZHT16BIG5およびKO16KS5601)の、わずかな文字についてのみ発生します。問題を回避するために、これらの文字に対する完全なラウンドトリップ変換は避けてください。
7.6 Unicodeを使用したODBCとOLE DBのプログラミング
Windowsプラットフォームを使用している場合は、OracleサーバーへのアクセスにOracle ODBCドライバまたはOracle Provider for OLE DBを使用する必要があります。この項では、これらのドライバによるUnicodeのサポート方法を説明します。次の内容について説明します。
7.6.1 ODBCとOLE DBのUnicode対応ドライバ
OracleのODBCドライバとOracle Provider for OLE DBは、データを消失することなく、Unicodeデータを正しく処理できます。たとえば、日本語フォントと日本語文字を入力するためのIMEをインストールしている場合は日本語データが含まれたUnicode ODBCアプリケーションを英語版Windowsで実行できます。
Oracleが提供しているODBCおよびOLE DB製品は、Windowsプラットフォーム専用です。UNIXプラットフォームについては、ベンダーにお問い合せください。
7.6.2 UnicodeでのOCI依存性
Unicodeデータを処理するために、ODBCドライバとOLE DBドライバは、OCI Unicodeのバインド機能と定義機能を使用します。OCI Unicodeデータのバインド機能と定義機能はNLS_LANG
の影響を受けません。つまり、プラットフォームのNLS_LANG
設定に関係なく、Unicodeデータは正しく処理されます。
関連項目:
7.6.3 UnicodeでのODBCとOLE DBのコード変換
通常は、サーバーと異なるデータ型をクライアントのデータ型に指定しないかぎり、冗長なデータ変換は発生しません。たとえば、UnicodeバッファSQL_C_WCHAR
をNCHAR
などのUnicodeデータ列にバインドすると、ODBCドライバとOLE DBドライバは、アプリケーションとOCIレイヤー間の変換を行いません。
フェッチする前にデータ型を指定せずに、クライアントのデータ型でSQLGetData
をコールすると、次の表に示す変換が行われます。
表7-7 ODBCの暗黙的なバインド・コード変換
ODBCクライアント・バッファのデータ型 | データベース内のターゲット列のデータ型 | フェッチ時の変換 | コメント |
---|---|---|---|
|
|
データベース文字セットが
|
予期しないデータ消失はありません。 データベース文字セットが |
|
|
データベース文字セットが OCIでの、データベース文字セットから データベース文字セットが OCIとODBCでの、データベース文字セット、UTF-16から |
予期しないデータ消失はありません。 データベース文字セットが |
挿入操作と更新操作のためには、データ型を指定する必要があります。
ODBCクライアント・バッファのデータ型は、SQLGetData
のコール時に提供されますが、即時ではありません。このため、SQLFetch
には情報はありません。
ODBCドライバではデータ整合性が保証されるため、暗黙的なバインドを実行すると、冗長な変換が行われ、パフォーマンスが低下する場合があります。明示的なバインドでパフォーマンスを優先するか、暗黙的なバインドで利便性を優先するかのいずれかを選択します。
7.6.3.1 OLE DBのコード変換
ODBCとは異なり、OLE DBでは、データの挿入、更新およびフェッチに対する暗黙的なバインドのみ実行できます。中間の文字セットを決定するための変換アルゴリズムは、ODBCの暗黙的なバインドの場合と同じです。
表7-8 OLE DBの暗黙的なバインド
OLE_DBクライアント・バッファのデータ型 | データベース内のターゲット列のデータ型 | インバインドとアウトバインドの変換 | コメント |
---|---|---|---|
|
|
データベース文字セットが OCIでの、データベース文字セットと データベース文字セットが OCIでの、データベース文字セットとUTF-16の間の変換 |
予期しないデータ消失はありません。 データベース文字セットが |
|
|
データベース文字セットが OCIでの、データベース文字セットから データベース文字セットが OCIでの、データベース文字セットとUTF-16の間の変換。OLE DBでの、UTF-16から |
予期しないデータ消失はありません。 データベース文字セットが |
7.6.4 ODBCのUnicodeデータ型
ODBCのUnicodeアプリケーションでは、SQLWCHAR
を使用してUnicodeデータを格納します。SQLWCHAR
のデータ操作には、すべての標準的なWindows Unicode関数を使用できます。たとえば、wcslen
はSQLWCHAR
データの文字数をカウントします。
SQLWCHAR sqlStmt[] = L"select ename from emp"; len = wcslen(sqlStmt);
Microsoft社のODBC 3.5仕様には、クライアントのSQL_C_WCHAR
、SQL_C_WVARCHAR
およびSQL_WLONGVARCHAR
に対して3つのUnicodeデータ型識別子が定義され、サーバーのSQL_WCHAR
、SQL_WVARCHAR
およびSQL_WLONGVARCHAR
に対して3つのUnicodeデータ型識別子が定義されています。
バインド操作の場合は、SQLBindParameter
を使用して、クライアントとサーバーの両方にデータ型を指定します。次に、Unicodeバインドの例を示します。クライアント・バッファName
は、Unicodeデータ(SQL_C_WCHAR
)がUnicode列(SQL_WCHAR
)に関連付けられている最初のバインド変数にバインドされていることを示しています。
SQLBindParameter(StatementHandle, 1, SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WCHAR, NameLen, 0, (SQLPOINTER)Name, 0, &Name);
次の表に、SQL NCHAR
データ型に対するサーバーのODBC Unicodeデータ型のデータ型マッピングを示します。
表7-9 サーバーのODBC Unicodeデータ型マッピング
ODBCデータ型 | Oracleデータ型 |
---|---|
|
|
|
|
|
|
SQL_WCHAR
、SQL_WVARCHAR
およびSQL_WLONGVARCHAR
は、ODBC仕様に従ってUnicodeデータとして処理されます。したがって、これらのデータ型はバイト数ではなく文字数で測定されます。
7.6.5 OLE DBのUnicodeデータ型
OLE DBでは、UnicodeのCクライアントに対してwchar_t
、BSTR
およびOLESTR
データ型が用意されています。実際には、wchar_t
が最も一般的なデータ型で、他のデータ型は特定の目的に使用されます。次の例では、静的なSQL文が割り当てられます。
wchar_t *sqlStmt = OLESTR("SELECT ename FROM emp");
OLESTR
マクロは、"L"修飾子と同様に機能してUnicode文字列を示します。OLESTR
を使用してUnicodeデータ・バッファを動的に割り当てる必要がある場合は、IMalloc
アロケート関数(例: CoTaskMemAlloc
)を使用します。ただし、OLESTR
の使用は、可変長データに対する通常の方法ではありません。一般的な文字列型には、かわりにwchar_t
*を使用してください。BSTR
も機能は同様です。BSTRは、文字列の前のメモリー位置に長さの接頭辞が付いている文字列です。一部の関数とメソッドでは、BSTR
Unicodeデータ型のみが受け入れられます。したがって、割当てを行うSysAllocString
やメモリーを解放するSysFreeString
などの特別な関数では、BSTR
Unicode文字列を操作する必要があります。
ODBCとは異なり、OLE DBではサーバーのデータ型を明示的に指定することはできません。クライアントのデータ型を設定すると、OLE DBドライバは、必要に応じて自動的にデータ変換を実行します。
次の表に、OLE DBのデータ型マッピングを示します。
表7-10 OLE DBのデータ型マッピング
OLE DBデータ型 | Oracleデータ型 |
---|---|
|
|
DBTYPE_BSTR
が指定されていると、DBTYPE_WCHAR
であるとみなされます。これは、両方ともUnicode文字列であるためです。
7.6.6 ADOアクセス
ADOは、OLE DBドライバとODBCドライバを使用してデータベースにアクセスするための高水準APIです。ほとんどのデータベース・アプリケーション開発者が、Windows上でADOインタフェースを使用しています。これは、このインタフェースが、Internet Information Server(IIS)用のActive Server Pages(ASP)に対する主要なスクリプト言語であるVisual Basicから簡単にアクセスできるためです。OLE DBおよびODBCドライバに対しては、ADOは単なるOLE DBコンシューマまたはODBCアプリケーションです。ADOは、OLE DBおよびODBCドライバがUnicode認識コンポーネントであることを前提としているため、常にUnicodeデータを操作しようとします。
7.7 Unicodeを使用したXMLプログラミング
グローバル市場向けのソフトウェア開発では、テキスト情報をあらゆる言語でやり取りできるように、UnicodeのXMLサポートが不可欠です。Unicodeでは、ほぼすべての文字と言語が一様にサポートされているため、XMLによる複数言語のサポートがはるかに容易になります。Oracle Database内のXMLでUnicodeを使用可能にするには、データベースの文字セットがUTF-8である必要があります。アプリケーションでUnicodeテキスト処理を使用可能にすることが、あらゆる言語をサポートするための基礎となります。各XML文書は、Unicode文字の既知のサブセットのみの使用が保証されないかぎり、Unicodeテキストであり、多言語が使用されている場合もあります。そのため、XMLにUnicodeを使用可能にすることをお薦めします。Unicodeサポートは、Javaおよび他の多数の最新プログラミング環境に用意されています。
この項には次のトピックが含まれます:
7.7.1 Javaを使用したUnicodeでのXMLファイルの作成
XMLファイルを読み書きする際の一般的な誤りは、文字の入出力にReader
およびWriter
クラスを使用することです。XMLファイルにReader
およびWriter
を使用すると、ランタイム環境のデフォルトの文字コード体系に基づく文字セット変換が必要になるため、この方法は使用しないでください。
たとえば、FileWriter
クラスを使用すると、文書がデフォルトの文字コード体系に変換されるため、問題が生じることがあります。ドキュメントにデフォルトの文字エンコーディングで使用できない文字が含まれる場合、出力ファイルでは、解析エラーやデータの損失が発生する可能性があります。
XML文書にはUTF-8が一般的ですが、通常、UTF-8はJavaのデフォルトのファイル・エンコーディングではありません。そのため、デフォルトのファイル・エンコーディングを想定するJavaクラスを使用すると、問題が発生する可能性があります。
次の例に、これらの問題を回避する方法を示します。
import java.io.*; import oracle.xml.parser.v2.*; public class I18nSafeXMLFileWritingSample { public static void main(String[] args) throws Exception { // create a test document XMLDocument doc = new XMLDocument(); doc.setVersion( "1.0" ); doc.appendChild(doc.createComment( "This is a test empty document." )); doc.appendChild(doc.createElement( "root" )); // create a file File file = new File( "myfile.xml" ); // create a binary output stream to write to the file just created FileOutputStream fos = new FileOutputStream( file ); // create a Writer that converts Java character stream to UTF-8 stream OutputStreamWriter osw = new OutputStreamWriter( fos, "UTF8" ); // buffering for efficiency Writer w = new BufferedWriter( osw ); // create a PrintWriter to adapt to the printing method PrintWriter out = new PrintWriter( w ); // print the document to the file through the connected objects doc.print( out ); } }
7.7.2 Javaを使用したUnicodeでのXMLファイルの読取り
XMLファイルをテキスト入力として読み取らないでください。ファイル・システムに格納されているXML文書の読取り時には、Parserを使用して文書の文字コード体系を自動的に検出します。Reader
クラスの使用や、入力ストリームでの文字コード体系の指定は避けてください。外部エンコーディング情報のないバイナリ入力ストリームの場合、ParserはXML文書のバイト順マークとエンコーディング宣言に基づいて文字コード体系を自動的に検出します。サポート対象のエンコーディングによる整形式の文書は、次のサンプル・コードを使用して正常に解析できます。
import java.io.*; import oracle.xml.parser.v2.*; public class I18nSafeXMLFileReadingSample { public static void main(String[] args) throws Exception { // create an instance of the xml file File file = new File( "myfile.xml" ); // create a binary input stream FileInputStream fis = new FileInputStream( file ); // buffering for efficiency BufferedInputStream in = new BufferedInputStream( fis ); // get an instance of the parser DOMParser parser = new DOMParser(); // parse the xml file parser.parse( in ); } }
7.7.3 Javaを使用したUnicodeでのXMLストリームの解析
XML文書のソースがファイル・システム以外の場合、通常、エンコーディング情報は文書の読取り前に使用可能です。たとえば、入力文書がJava文字ストリームまたはReaderの形式で提供される場合、そのエンコーディングは明らかであり、検出する必要はありません。Parserは、文字コード体系に関係なくUnicodeでReaderの解析を開始できます。
次に、外部エンコーディング情報を使用した文書の解析例を示します。
import java.io.*; import java.net.*; import org.xml.sax.*; import oracle.xml.parser.v2.*; public class I18nSafeXMLStreamReadingSample { public static void main(String[] args) throws Exception { // create an instance of the xml file URL url = new URL( "http://myhost/mydocument.xml" ); // create a connection to the xml document URLConnection conn = url.openConnection(); // get an input stream InputStream is = conn.getInputStream(); // buffering for efficiency BufferedInputStream bis = new BufferedInputStream( is ); /* figure out the character encoding here */ /* a typical source of encoding information is the content-type header */ /* we assume it is found to be utf-8 in this example */ String charset = "utf-8"; // create an InputSource for UTF-8 stream InputSource in = new InputSource( bis ); in.setEncoding( charset ); // get an instance of the parser DOMParser parser = new DOMParser(); // parse the xml stream parser.parse( in ); } }