[目次] [前の項目] [次の項目]

SQL と Java の型のマッピング

注: この章の内容は、Addison Wesley 社より Java シリーズの 1 巻として出版された『JDBCTM API Tutorial and Reference, Second Edition: Universal Data Access for the JavaTM 2 Platform』(ISBN 0-201-43328-1) に基づいて作成したものです。

8.1 マッピングの概要

SQL のデータ型は、Java プログラミング言語のデータ型と同一ではないので、Java の型を使用するアプリケーションと SQL の型を使用するデータベースの間で、データを移し変えるための機構が必要です。(本文中で使われている「Java の型」という表現は、「Java プログラミング言語上の型」を表しています。)

Java プログラミング言語で記述されているアプリケーションとデータベースの間でデータを移し換えるために、JDBC API では、次の 3 つのメソッドを提供しています。

  1. SQL SELECT の結果を Java の型として取得するための ResultSet クラスのメソッド
  2. Java の型を SQL 文のパラメータとして送信するための PreparedStatement クラスのメソッド
  3. SQL OUT パラメータを Java の型として取得するための CallableStatement クラスのメソッド

この節では、さまざまなクラスやインタフェースに影響するデータ型についての情報をまとめ、SQL の型と Java の型の間のマッピングを示す一覧を参照しやすいように 1 箇所に置きました。また、SQL3 の型を含む、個々の汎用的な SQL のデータ型についても説明します。

8.2 SQL の型から Java の型へのマッピング

異なるデータベース製品がサポートする SQL の型の間には、相当な相違があります。異なるデータベースが同一の意味を持つ SQL の型をサポートしている場合でも、それらの型に異なる名前を与えていることがあります。たとえば、主要データベースのほとんどが大きなバイナリ値に対する SQL の型をサポートしていますが、Oracle ではこの型を LONG RAW、Sybase では IMAGE、Informix では BYTE、DB2 では LONG VARCHAR FOR BIT DATA とそれぞれ呼んでいます。

JDBC プログラマは、通常は、ターゲットのデータベースが使用している実際の SQL の型名に気を使う必要はありません。多くの場合、JDBC プログラマは、既存のデータベースのテーブルに対してプログラミングをし、そうしたテーブルを作成した正確な SQL の型名に注意を払う必要はありません。

JDBC は、クラス java.sql.Types で汎用的な SQL の型識別子のセットを定義しています。そのセットの型は、もっとも一般的に使用される SQL の型を表すように設計されています。JDBC API によるプログラミングでは、プログラマは通常、ターゲットのデータベースが使用している正確な SQL の型名を意識することなく、そのセットの JDBC 型を使用して汎用的な SQL の型を参照することができます。それらの JDBC 型は、次の節で詳しく説明します。

プログラマが SQL の型名を使用する必要があるのは、主に新しいデータベースのテーブルを作成する場合の SQL CREATE TABLE 文の中です。この場合には、プログラマは、そのターゲットのデータベースがサポートしている SQL の型名を使用するように注意する必要があります。「データベース固有の SQL の型にマッピングされる JDBC の型」の表は、主要なデータベースで JDBC の型として使用されている適切な SQL の型名に対する提案を示しています。特定のデータベースでのさまざまな SQL の型の動作の正確な定義を必要とする場合には、そのデータベースのマニュアルを参照することをお勧めします。

さまざまな異なるデータベース上でテーブルを作成できる移植性の高い JDBC プログラムを作成したい場合には、2 つの主な選択肢があります。1 つ目は、INTEGERNUMERIC、または VARCHAR のようなすべてのデータベースに対して稼動する可能性の高い、非常に広範囲で受け入れられている SQL の型名だけを使用するように制限することです。2 つ目の選択肢は、java.sql.DatabaseMetaData.getTypeInfo メソッドを使用して、どの SQL の型をそのデータベースが実際にサポートしているかを発見し、指定の JDBC 型に一致するデータベース固有の SQL の型名を選択することです。

JDBC は、JDBC データベース型から Java への標準的なマッピングを定義します。たとえば、JDBC INTEGER は通常、Java int にマッピングされます。これは、JDBC の値を単純な Java の型として読み書きする単純なインタフェースをサポートします。

Java の型は、JDBC の型と正確に同じである必要はありません。 パラメータを正確に格納したり取り出したりし、SQL 文からの結果を復旧するのに十分な型の情報でそれらを表現できればよいだけです。たとえば、Java の String オブジェクトは、JDBC CHAR 型のどれにも厳密には一致しませんが、CHARVARCHAR、または LONGVARCHAR を正常に表現するのに十分な型情報を与えます。

8.3 基本的な JDBC の型

ここでは、JDBC 1.0 と 2.0 API の両方でサポートされている JDBC のデータ型について説明します。また、標準の SQL の型および Java プログラミング言語の型との関係についても説明します。JDBC 2.0 コア API で導入された新しい JDBC のデータ型は、8.4 節で説明します。

8.3.1 CHAR、VARCHAR、および LONGVARCHAR

JDBC の型 CHARVARCHAR、および LONGVARCHAR は密接に関連しています。CHAR は短い固定長の文字列を、VARCHAR は短い可変長の文字列を、LONGVARCHAR は長い可変長の文字列をそれぞれ表します。

JDBC CHAR に対応する SQL CHAR 型は SQL-92 で定義され、すべての主要なデータベースによってサポートされています。文字列の長さを指定するパラメータを取ります。したがって、CHAR(12) は 12 文字長の文字列を定義します。すべての主要なデータベースは、CHAR 長を最高 254 文字までサポートしています。

JDBC VARCHAR に対応する SQL VARCHAR 型は SQL-92 で定義され、すべての主要なデータベースによってサポートされています。文字列の最大長を指定するパラメータを取ります。したがって、VARCHAR(12) はその長さが最高 12 文字長の文字列を定義します。すべての主要なデータベースは、VARCHAR の長さを最高 254 文字までサポートしています。文字列値が VARCHAR 変数に割り当てられると、データベースは割り当てられた文字列の長さを記憶し、それの SELECT 時に正確に元の文字列を返します。

JDBC LONGVARCHAR 型には、一貫した SQL マッピングが存在しません。すべての主要なデータベースは、少なくとも 1G バイトまでサポートする、ある種の非常に長い可変長の文字列をサポートしますが、SQL の型名は異なります。例については、「データベース固有の SQL の型にマッピングされる JDBC の型」の表を参照してください。

Java のプログラマは、CHARVARCHAR、および LONGVARCHAR の JDBC 文字列の 3 つの型を区別する必要がありません。それぞれは Java の String として表現することができ、必要とされた正確なデータ型を知らなくても SQL 文を正しく読み書きすることができます。

CHARVARCHAR、および LONGVARCHAR は、String または char[] のどれにでもマッピングすることができますが、String の方が通常の使用のためにはより適切です。また、String クラスにより、Stringchar[] の間の変換がより簡単になります。String オブジェクトを char[] に変換するメソッドがあり、また char[]String オブジェクトに調整するコンストラクタもあります。

対処すべき問題の 1 つは、CHAR(n) 型の固定長 SQL 文字列をどのように処理するかです。最適なのは、JDBC ドライバ (または DBMS) が空白で適切なパディングを行うことです。したがって CHAR(n) フィールドがデータベースから取り出されたとき、ドライバがそれを長さが n の Java の String オブジェクトに変換しますが、これには末尾にパディングの空白がいくつか含まれている可能性があります。これとは逆に、String オブジェクトが CHAR(n) フィールドに送信されると、ドライバまたはデータベース、あるいはその両方が必要なパディング用空白を文字列の末尾に追加し、その長さを n にします。

メソッド ResultSet.getString は、新しい String オブジェクトを割り当てたり返したりしますが、データを CHARVARCHAR、および LONGVARCHAR フィールドから取り出すことをお勧めします。これは通常のデータを取り出すには適切ですが、JDBC の LONGVARCHAR 型を使用して何メガバイトかの文字列を保存する場合は扱いにくいことがあります。このようなケースを処理するために、ResultSet インタフェースの 2 つのメソッドにより、プログラマが LONGVARCHAR の値を任意のサイズの塊でシーケンシャルにデータを読み取れる Java 入力ストリームとして取り出すことができるようにしています。これらのメソッドは getAsciiStreamgetCharacterStream で、LONGVARCHAR 列に保存されているデータを ASCII または Unicode 文字のストリームとして配信します。メソッド getUnicodeStream は、推奨されていません。

後述する SQL3 の CLOB データ型を使って、大量の文字データを表すこともできます。

8.3.2 BINARY、VARBINARY、および LONGVARBINARY

JDBC の型 BINARYVARBINARY、および LONGVARBINARY は密接に関連しています。BINARY は小さい固定長のバイナリ値を、VARBINARY は小さな可変長のバイナリ値を、LONGVARBINARY は大きな可変長のバイナリ値をそれぞれ表します。

以上の BINARY 型は標準化されておらず、サポートは主要なデータベース間で相当に変動します。

JDBC BINARY に対応する SQL BINARY 型は、非標準の SQL の拡張で、一部のデータベースに実装されているにすぎません。バイナリバイトの数を指定するパラメータを取ります。したがって、BINARY(12) は 12 バイトのバイナリ型を定義します。通常、BINARY 値は 254 バイトに限定されています。

JDBC VARBINARY に対応する SQL VARBINARY 型は、非標準の SQL の拡張で、一部のデータベースに実装されているにすぎません。バイナリバイトの最大数を指定するパラメータを取ります。したがって、VARBINARY(12) はその長さの最大長が 12 バイトであるバイナリ型を定義します。通常、VARBINARY 値は 254 バイトに限定されています。バイナリ値が VARBINARY 変数に割り当てられると、データベースは割り当てられた値の長さを記憶し、それの SELECT 時に、元の値を正確に返します。

JDBC LONGVARBINARY 型に対応する一貫した SQL の型は存在しません。すべての主要なデータベースは、少なくとも 1G バイトのデータをサポートする、ある種の非常に大きな可変長のバイナリ型をサポートしますが、その SQL の型名は異なります。例については、「データベース固有の SQL の型にマッピングされる JDBC の型」の表を参照してください。

BINARYVARBINARY、および LONGVARBINARY は、Java プログラミング言語では byte 配列として、すべて同じように表現できます。要求されている BINARY データ型を正確に知らなくても、SQL 文を正しく読み書きできるため、Java プログラミング言語でコードを記述するプログラマがそれらの型を区別する必要はありません。

BINARYVARBINARY の値を取り出すために推奨されるメソッドは、ResultSet.getBytes です。JDBC LONGVARBINARY 型の列が、何メガバイト長ものバイト配列を保存している場合には、getBinaryStream メソッドをお勧めします。LONGVARCHAR の場合と同様に、このメソッドはプログラマが LONGVARBINARY 値を、あとでより小さな塊で読むことができる Java 入力ストリームとして取り出すことを可能にします。

後述する SQL3 の BLOB データ型を使って、大量のバイナリデータを表すこともできます。

8.3.3 BIT

JDBC 型 の BIT は、0 か 1 を取り得る単一のビット値を表します。

SQL-92 は、SQL BIT 型を定義します。しかし、JDBC BIT 型とは異なり、この SQL-92 BIT 型は、固定長のバイナリ列を定義するパラメータ化した型として使用することができます。SQL-92 は、単一のビットを表すのに単純な非パラメータ化 BIT 型の使用も許しています。 この使用は、JDBC BIT 型に対応しています。SQL-92 BIT 型は、「完全な」 SQL-92 においてだけ要求され、現在、主要なデータベースの一部にしかサポートされていません。したがって、移植性を望むコードでは、広くサポートされている JDBC SMALLINT 型の方を使用することをお勧めします。

JDBC BIT 型に対して推奨される Java マッピングは、Java の boolean 型とするものです。

8.3.4 TINYINT

JDBC 型の TINYINT は、0 から 255 までの符号付きまたは符号なしの 8 ビットの整数値を表します。

対応する SQL の型の TINYINT は現在、主要なデータベースの一部でしかサポートされていません。したがって、移植性を望むコードでは、広くサポートされている JDBC SMALLINT 型の方を使用することをお勧めします。

JDBC TINYINT 型に対して推奨されている Java マッピングは、Java byte か Java short のどちらかです。8 ビットの Java byte 型は -128 から 127 までの符号付きの値を表すので、より大きな TINYINT 値に対して常に適切になるとは限りません。 ただし、16 ビットの Java short は常にすべての TINYINT 値を保持することができます。

8.3.5 SMALLINT

JDBC 型の SMALLINT は、-32768 から 32767 までの 16 ビットの符号付き整数値を表します。

対応する SQL の型の SMALLINT は SQL-92 で定義され、すべての主要なデータベースによってサポートされています。SQL-92 標準では、SMALLINT の精度を実装に任せていますが、実際には、すべての主要なデータベースは少なくとも 16 ビットをサポートしています。

JDBC SMALLINT 型に対して推奨される Java マッピングは、Java short とするものです。

8.3.6 INTEGER

JDBC 型の INTEGER は、-2147483648 から 2147483647 までの 32 ビットの符号付き整数値を表します。

対応する SQL の型の INTEGER は SQL-92 で定義され、すべての主要なデータベースによって広くサポートされています。SQL-92 標準では、INTEGER の精度を実装に任せていますが、実際には、すべての主要なデータベースは少なくとも 32 ビットをサポートしています。

JDBC INTEGER 型に対して推奨される Java マッピングは、Java int とするものです。

8.3.7 BIGINT

JDBC 型の BIGINT は、-9223372036854775808 から 9223372036854775807 までの 64 ビットの符号付き整数値を表します。

対応する SQL の型の BIGINT は、SQL の非標準の拡張です。実際、SQL BIGINT 型は、主要などのデータベースにも現在実装されていません。 したがって、移植性を望むコードでは、使用を避けることをお勧めします。

JDBC BIGINT 型に対して推奨される Java マッピングは、Java long とするものです。

8.3.8 REAL

JDBC 型の REAL は、7 桁の仮数部をサポートする「単精度」の浮動小数点数です。

対応する SQL の型の REAL は SQL-92 で定義され、すべての主要なデータベースによって、一般的ではありませんが、広くサポートされています。SQL-92 標準では、REAL の精度を実装に任せていますが、実際には、REAL をサポートするすべての主要なデータベースは少なくとも 7 桁の仮数精度をサポートしています。

JDBC REAL 型に対して推奨される Java マッピングは、Java float とするものです。

8.3.9 DOUBLE

JDBC 型の DOUBLE は、15 桁の仮数部をサポートする「倍精度」の浮動小数点数です。

対応する SQL の型は、DOUBLE PRECISION であり SQL-92 で定義され、すべての主要なデータベースによって、広くサポートされています。SQL-92 標準では、DOUBLE PRECISION の精度を実装に任せていますが、実際には、DOUBLE PRECISION の精度をサポートするすべての主要なデータベースは少なくとも 15 桁の仮数精度をサポートしています。

JDBC DOUBLE 型に対して推奨される Java マッピングは、Java double とするものです。

8.3.10 FLOAT

JDBC 型の FLOAT は、基本的には JDBC 型の DOUBLE と等価です。FLOATDOUBLE の両方を提供したのは、以前のデータベースの API との一貫性を維持しようとしたためですが、混乱を起こす危険性が考えられます。FLOAT は、15 桁の仮数部をサポートする「倍精度」浮動小数点数です。

対応する SQL の型の FLOAT は SQL-92 で定義されています。SQL-92 標準では、FLOAT の精度を実装に任せていますが、実際には、FLOAT をサポートするすべての主要なデータベースは少なくとも 15 桁の仮数精度をサポートしています。

この FLOAT 型に対して推奨される Java マッピングは、Java double とするものです。ただし、倍精度の SQL FLOAT と単精度の Java float との間の混乱が予想されるため、JDBC プログラマは、通常の場合には、FLOAT よりも JDBC DOUBLE 型を使用することをお勧めします。

8.3.11 DECIMAL と NUMERIC

JDBC 型の DECIMALNUMERIC は非常に似ています。両方とも、固定精度の 10 進数を表します。 対応する SQL の型 DECIMAL および NUMERIC は、SQL-92 で定義されており、広範囲に実装されています。これらの SQL の型は、精度とスケールのパラメータを取ります。精度は、サポートされている 10 進数の総桁数で、スケールは、小数点以下の桁数です。ほとんどの DBMS では、スケールは精度以下です。たとえば、「12.345」の精度は 5 であり、スケールは 3 となります。 「.11」の精度は 2 であり、スケールは 2 となります。 JDBC は、すべての DECIMAL の型 と NUMERIC の型が、少なくとも 15 桁の精度とスケールをサポートすることを要求しています。

DECIMALNUMERIC の唯一の相違は、SQL-92 仕様が、NUMERIC 型が正確に指定の精度で表現されることを要求する一方で、DECIMAL 型では、型の生成時に指定された精度を超えた精度を追加することを実装に許している点にあります。したがって、型の NUMERIC(12,4) で作成された列は、常に正確に 12 桁で表され、型の DECIMAL(12,4) で作成された列は、より大きな桁数で表されることもあります。

DECIMAL 型と NUMERIC 型に推奨される Java マッピングは、java.math.BigDecimal です。java.math.BigDecimal 型は、BigDecimal 型をほかの BigDecimal 型、整数型、および浮動少数点型と加減乗除できる、算術計算を提供しています。

DECIMALNUMERIC の値を取り出すために推奨されるメソッドは、ResultSet.getBigDecimal です。JDBC は、単純な Strings または char の配列としてこれらの SQL の型へのアクセスも可能にします。したがって、Java プログラマは、getString を使用して NUMERIC または DECIMAL の結果を受け取ることができます。ただし、これにより、アプリケーションの作成者が文字列上で算術計算をすることが必要になるので、DECIMAL または NUMERIC を通貨の値として使用する一般的な場合が、むしろ扱いにくくなります。これらの SQL の型を Java の数値型のどれかとして取り出すことも可能です。

8.3.12 DATE、TIME、および TIMESTAMP

時間に関連する JDBC の型には以下の 3 つがあります。

標準の Java クラス java.util.Date は、これら 3 つの JDBC の型に正確には一致していないので (標準の Java クラスには DATETIME の情報はあるが、ナノ秒はない)、JDBC は java.util.Date の 3 つのサブクラスを定義して、SQL の型に対応しています。これらは以下のとおりです。

JDBC の 3 つの時間関連のクラスはすべて java.util.Date のサブクラスなので、java.util.Date が期待されている場所で使用することができます。たとえば、国際化メソッドは java.util.Date オブジェクトを引数として取るので、JDBC 時間関連クラスのどれかのインスタンスとして渡すことができます。

JDBC Timestamp オブジェクトには、その親の日付と時間の構成要素と、それとは別にナノ秒の構成要素もあります。java.util.Date オブジェクトが期待されている箇所で、java.sql.Timestamp オブジェクトを使用すると、ナノ秒の構成要素は失われます。ただし、java.util.Date オブジェクトが 1 ミリ秒の精度で保存されているので、java.sql.Timestamp オブジェクトを java.util.Date オブジェクトに変換したときにこの程度の精度を保つことは可能です。これは、ナノ秒構成要素の中のナノ秒を (ナノ秒の数値を 1,000,000 で割ることにより ) ミリ秒に換算してから、結果を java.util.Date オブジェクトに加算します。999,999 ナノ秒まではこの変換によって失われますが、結果として生じる java.util.Date オブジェクトは 1 ミリ秒以内の誤差の精度を持ちます。

次の部分的なコードは、1 ミリ秒以内の精度を持つ java.util.Date オブジェクトに java.sql.Timestamp オブジェクトを変換する例です。

Timestamp t = new Timestamp(98724573287540L);
java.util.Date d;
d = new java.util.Date(t.getTime() + (t.getNanos() / 1000000));

JDBC 2.0 コア API の新しいメソッドを使用すると、ドライバによって日付、時刻、またはタイムスタンプが計算されるときに、指定されたタイムゾーンが考慮されます。タイムゾーン情報は、java.util.Calendar オブジェクトに組み込まれています。 このオブジェクトは、DateTime、および Timestamp の値の取得および設定に使われる新しいバージョンのメソッドに渡されます。タイムゾーンが指定されていない場合は、日付、時刻、およびタイムスタンプの計算時にアプリケーションが動作している Virtual Machine のタイムゾーンが使われます。

8.4 進歩した JDBC データ型

ISO (International Organization for Standardization) および IEC (International Electrotechnical Commission) は、一般的に SQL3 の型と呼ばれる新しいデータ型を定義しています。これらの新しい SQL3 のデータ型のうち、BLOBCLOBARRAY、および REF はあらかじめ定義されている型です。 これに対し、SQL の構造化型および DISTINCT 型は、ユーザ定義の型 (UDT) です。これらの新しい型は、DISTINCT 以外は JDBC 2.0 コア API の新しいインタフェースにマッピングされます。ここでは、各データ型について簡単に説明します。 各型についての詳細は、該当するインタフェースのリファレンスの章にあります。DISTINCT データ型についての章もありますが、DISTINCT 型は組み込みの型にマッピングされるため、個々のインタフェースはありません。

JDBC 2.0 コア API の新しいデータ型は、リレーショナルデータベースで使われるデータ型が大幅に拡張されたものです。一般に、これらのデータ型は、オブジェクトにより一層似ています。実際、新しいデータ型の中の 2 つのデータ型は UDT なので、Java プログラミング言語のクラスにカスタムマッピングすることができます。3 つ目の UDT (JAVA_OBJECT) は、Java プログラミング言語で定義されたクラスのインスタンスです。JDBC 2.0 コア API の新しいデータ型は、高度な機能を持っていますが、すべて JDBC 1.0 API のデータ型と同じように便利に使用することができます。たとえば、これらのデータ型をデータベーステーブルの列の値として使い、適切な getXXX および setXXX メソッドを使って、取り出したり、保存したりすることができます。

8.4.1 BLOB

JDBC の型 BLOB は、SQL3 の BLOB (Binary Large Object) を表します。

JDBC BLOB の値は、Java プログラミング言語の Blob インタフェースのインスタンスにマッピングされます。ドライバが標準的な実装に準拠している場合、Blob オブジェクトは、オブジェクトのバイナリデータを含まず、サーバ上の BLOB 値を論理的に指すので、効率が大きく向上します。Blob インタフェースは、必要な時に BLOB データをクライアント上に実体化するためのメソッドを提供しています。

8.4.2 CLOB

JDBC の型 CLOB は、SQL3 の型 CLOB (Character Large Object) を表します。

JDBC CLOB の値は、Java プログラミング言語の Clob インタフェースのインスタンスにマッピングされます。ドライバが標準的な実装に準拠している場合、Clob オブジェクトは、オブジェクトの文字データを含まず、サーバ上の CLOB 値を論理的に指すので、効率が大きく向上します。Clob インタフェースの 2 つのメソッドによって、 CLOB オブジェクトのデータがクライアント上に実体化されます。

8.4.3 ARRAY

JDBC の型 ARRAY は、SQL3 の型 ARRAY を表します。

ARRAY の値は、Java プログラミング言語の Array インタフェースのインスタンスにマッピングされます。ドライバが標準的な実装に準拠している場合は、ARRAY オブジェクトは、オブジェクトの要素を含まず、サーバ上の ARRAY 値を論理的に指すので、効率が大きく向上します。Array インタフェースには、クライアント上の ARRAY オブジェクトの要素を、配列または ResultSet オブジェクトの形式で実体化するメソッドが含まれています。

8.4.4 DISTINCT

JDBC の型 DISTINCT フィールド ( クラス) > DISTINCT は、SQL3 の型 DISTINCT を表します。

標準的なマッピングでは、DISTINCT 型は、DISTINCT オブジェクトの基底型がマッピングされる Java の型にマッピングされます。たとえば、基底型が CHARDISTINCT 型は、String オブジェクトにマッピングされます。 基底型が SQL INTEGERDISTINCT 型は、int にマッピングされます。

DISTINCT 型は、Java プログラミング言語にカスタムマッピングすることもできます。カスタムマッピングは、SQLData インタフェースを実装するクラス、および、java.util.Map オブジェクト内のエントリで構成されます。

8.4.5 STRUCT

JDBC の型 STRUCT は、SQL3 の構造化型を表します。SQL の構造化型は、ユーザによって CREATE TYPE 文を使って定義される、1 つ以上の属性で構成される型です。これらの属性には、組み込み済みまたはユーザ定義の任意の SQL のデータ型を指定できます。

標準的なマッピングでは、SQL の型 STRUCT は、Java プログラミング言語の Struct オブジェクトにマッピングされます。Struct オブジェクトには、オブジェクトが表す STRUCT 値の各属性の値が含まれます。

STRUCT 値は、Java プログラミング言語のクラスにカスタムマッピングできます。STRUCT 内の各属性は、クラスのフィールドにマッピングできます。カスタムマッピングは、SQLData インタフェースを実装するクラス、および、 java.util.Map オブジェクト内のエントリで構成されます。

8.4.6 REF

JDBC の型 REF は、SQL3 の型 REF を表します。SQL REF は、SQL の構造化型のインスタンスを参照、つまり論理的に指します。 このインスタンスは、REF によって持続的および一意に識別されます。Java プログラミング言語では、Ref インタフェースは SQL REF を表します。

クライアント上に実体化した属性値を持たないようにして、アプリケーションからデータベースの SQL 構造化型のインスタンスを指すようにしたい場合には、REF 型、つまり SQL 構造化型への参照を使うことができます。

REF 値は、SQL 構造化型の特定のインスタンスに対して特別に作成された一意の識別子です。この値は、値が参照するインスタンスとともに、サーバ上の特定のテーブルに永続的に保存されます。アプリケーションからその特別なテーブルの REF 値を select すれば、その REF 値によって識別される構造化型のインスタンスの代わりに、その REF 値を使用できます。

8.4.7 JAVA_OBJECT

JDBC 2.0 コア API に追加された JDBC 型の JAVA_OBJECT を使うと、Java プログラミング言語のオブジェクトを、データベースの値として簡単に使うことができます。JAVA_OBJECT は、Java プログラミング言語で定義されているクラスのインスタンスに対する単なる型のコードで、データベースのオブジェクトとして保存されます。JAVA_OBJECT 型は、Java オブジェクトを直接保存できるように型システムが拡張されているデータベースで使われます。JAVA_OBJECT の値は、直列化された Java オブジェクトとして、またはベンダー固有の形式で保存されます。

JAVA_OBJECT 型は、getTypeInfogetColumnsgetUDTs などを含めた、さまざまな DatabaseMetaData メソッドから返される ResultSet オブジェクト内の DATA_TYPE 列の取り得る値の 1 つです。新しい JDBC 2.0 コア API の一部である getUDTs メソッドは、適切なパラメータが渡されると、特定のスキーマに保存されている Java オブジェクトの情報を返します。この情報が利用できると、Java クラスをデータベースの型として簡単に使うことができます。

JAVA_OBJECT 型の値がサポートされる DBMS の場合は、値は PreparedStatement.setObject メソッドを使ってデータベーステーブルに保存されます。この値は、ResultSet.getObject または CallableStatement.getObject メソッドを使って取り出し、ResultSet.updateObject メソッドを使って更新します。

たとえば、Engineer クラスのインスタンスが PERSONNEL テーブルの ENGINEERS 列に保存されていると仮定すると、次の部分的なコードは、技術者の名前をすべて出力します。 stmt は、Statement オブジェクトです。

ResultSet rs = stmt.executeQuery("SELECT ENGINEERS FROM PERSONNEL");
while (rs.next()) {
	Engineer eng = (Engineer)rs.getObject("ENGINEERS");
	System.out.println(eng.lastName + ", " + eng.firstName);
}

Engineer のすべてのインスタンスを含む ResultSet オブジェクト rs がクエリーから返され、getObject メソッドによって各インスタンスが順番に取り出されます。getObject から返される値は Object 型なので、変数 eng に代入する前に、詳細な Engineer 型にナロー変換する必要があります。

8.5 マッピングの例

Java プログラミング言語で記述されたプログラムがデータをデータベースから取り出すという状況では、必ずなんらかの形でマッピングとデータ変換が必要です。ほとんどの場合、JDBC API を使うプログラマは、自分が対象としているデータベースの仕組みについての知識を持っています。つまり、データベースにどのようなテーブルがあり、それらのテーブルの各列のデータ型が何かなどについて知っています。したがって、ResultSetPreparedStatement、および CallableStatement のインタフェースで固定的に入力したアクセスメソッドを使用することができます。この節では、3 つの異なるシナリオを示すとともに、それぞれの場合に必要なデータのマッピングと変換について説明します。

8.5.1 簡単な SQL 文

もっとも一般的なケースでは、ユーザが簡単な SQL 文を実行して、結果を持つ ResultSet オブジェクトを受け取ります。データベースが返し、ResultSet 列に保存される各値は、JDBC のデータ型を持っています。ResultSet.getXXX メソッドへの呼び出しは、その値を Java のデータ型として取り出します。たとえば、ResultSet 列に JDBC の FLOAT 値が入っている場合、メソッド getDouble はその値を Java の double として取り出します。表 8.6 は、どの getXXX メソッドがどの JDBC の型を取り出すために使用されるかを示します。ResultSet 列の型がわからないユーザは、メソッド ResultSet.getMetaData を呼び出してから、ResultSetMetaData.getColumnType を呼び出すことにより、その情報を取得することができます。

8.5.2 IN パラメータを取る SQL 文

もう 1 つのシナリオでは、ユーザが入力パラメータを取る SQL クエリーを送信します。この場合、ユーザは PreparedStatement.setXXX メソッドを呼び出して、各入力パラメータに値を割り当てます。たとえば、PreparedStatement.setLong(1, 2345678) は、最初のパラメータに 2345678 という値を Java の long として割り当てます。ドライバは、データベースに送信するために、2345678 を JDBC の BIGINT に変換します。ドライバがデータベースにどの JDBC の型を送信するかは、Java の型から JDBC の型への標準的なマッピングによって決定されます。 この標準的なマッピングは、表 8.2 に示されています。

8.5.3 INOUT パラメータを取る SQL 文

さらにもう 1 つのシナリオでは、ユーザがストアドプロシージャを呼び出し、値をその INOUT パラメータに割り当て、ResultSet オブジェクトから値を取り出し、パラメータから値を取り出します。このケースはあまり一般的ではなく、通常の場合より複雑ですが、マッピングとデータ変換の好例となるでしょう。

このシナリオではまず、PreparedStatement.setXXX メソッドを使用して、INOUT パラメータに値を割り当てます。加えて、パラメータも出力に使用されるので、プログラマはデータベースが返す値の JDBC 型とともに各パラメータを登録する必要があります。これは、クラス Types に定義された JDBC の型の 1 つを取るメソッド CallableStatement.registerOutParameter を使って行います。プログラマは、CallableStatement.getXXX メソッドを使って、出力パラメータに保存された値を取り出します。

CallableStatement.getXXX で使用される XXX 型は、そのパラメータに登録された JDBC の型にマッピングする必要があります。たとえば、データベースが、JDBC REAL という型の出力値を返すと期待されている場合、パラメータは java.sql.Types.REAL として登録されている必要があります。次に JDBC REAL 値を取り出すためには、メソッド CallableStatement.getFloat を呼び出す必要があります (JDBC の型から Java の型へのマッピングは、表 8.1 に示されています)。メソッド getFloat は、出力パラメータに保存されている値を JDBC REAL から Java float に変換してから返します。多様なデータベースに対応し、アプリケーションの移植性を高めるために、値を出力パラメータから取り出す前に、値を ResultSet オブジェクトから取り出すことをお勧めします。

以下のコードは、ともに INOUT パラメータが 2 つ付いていて、通常の JDBC ResultSet を返す getTestData という名前のストアドプロシージャを呼び出す方法を示します。まず、Connection オブジェクト con が、CallableStatement オブジェクト cstmt を生成します。次に、メソッド setByte が最初のパラメータに Java byte25 を設定します。ドライバは、25 を JDBC TINYINT に変換し、データベースに送信します。メソッド setBigDecimal が、2 番目のパラメータを 83.75 という入力値に設定します。ドライバは、この Java BigDecimal オブジェクトを JDBC NUMERIC の値に変換します。次に、2 つのパラメータが OUT パラメータとして登録され、最初のパラメータが JDBC TINYINT、2 番目のパラメータが小数点第 2 位までを持つ JDBC NUMERIC になります。cstmt が実行されたあとで、ResultSet.getXXX を使用して ResultSet オブジェクトから値が取り出されます。メソッド getString は最初の列の値を Java String オブジェクトとして取得し、getInt は 2 番目の列の値を Java int として取得し、2 つ目の getInt は 3 番目の列の値を Java int として取得します。

次に、CallableStatement.getXXX メソッドが、出力パラメータに保存された値を取り出します。メソッド getByte が JDBC TINYINT を Java byte として取り出し、getBigDecimal が小数点第 2 位までを持つ JDBC NUMERIC を、Java BigDecimal オブジェクトとして取り出します。パラメータが入出力パラメータの場合、setXXX メソッドは getXXX と同じ Java 型のものを使用します (setByte および getByte と同様)。registerOutParameter メソッドは、それを Java の型からマッピングされる JDBC の型に登録します。 Java byte は、表 8.2 で示されるように、JDBC TINYINT にマッピングされます。

CallableStatement cstmt = con.prepareCall(
	"{call getTestData(?, ?)}");
cstmt.setByte(1, 25);
cstmt.setBigDecimal(2, 83.75);
// register the first parameter as a JDBC TINYINT and the second
// as a JDBC NUMERIC with two digits after the decimal point
cstmt.registerOutParameter(1, java.sql.Types.TINYINT);
cstmt.registerOutParameter(2, java.sql.Types.NUMERIC, 2);
ResultSet rs = cstmt.executeQuery();
// retrieve and print values in result set
while (rs.next()) { 	
	String name = rs.getString(1);
	int score = rs.getInt(2);
	int percentile = rs.getInt(3);
	System.out.print("name = " + name + ", score = " + score);

	System.out.println(", percentile = " + percentile);
}
// retrieve values in output parameters	
byte x = cstmt.getByte(1); 
java.math.BigDecimal n = cstmt.getBigDecimal(2); 

一般化すると、CallableStatement.getXXXCallableStatement.setXXX メソッドの XXX は Java の型です。setXXX メソッドについては、データベースに送信する前に、(表 8.2 に示された標準的なマッピングを使用して) ドライバが Java の型を JDBC の型に変換します。getXXX メソッドについては、getXXX メソッドに返される前に、データベースによって返された JDBC の型をドライバが (表 8.1 に示された標準的なマッピングを使用して) Java の型に変換します。

メソッド registerOutParameter は常に、JDBC の型を引数として取り、メソッド setObject は JDBC の型を引数として取ります。

オプションの 3 番目の引数に、JDBC の型が供給される場合、メソッド setObject により、パラメータの値が Java の型から指定された JDBC の型に明示的に変換されることに注意してください。変換後の JDBC の型が setObject に供給されていない場合は、パラメータ値は Java の Object 型からの標準的なマッピングの JDBC の型に変換されます (表 8.4 を参照)。ドライバは、パラメータをデータベースに送信する前に、明示的または暗黙的な変換を行います。

8.6 カスタムマッピング

SQL3 のユーザ定義型 (UDT) である構造化型および DISTINCT 型は、Java プログラミング言語のクラスにカスタムマッピングできます。カスタムマッピングが設定されている場合、ドライバは UDT に対して JDBC と Java 間の型変換を行うときに、標準的なマッピングの代わりにカスタムマッピングを使います。

UDT は、ResultSet.getObject および CallableStatement.getObject メソッドを使ってデータベースから取り出され、PreparedStatement.setObject メソッドを使って元のデータベースに返信されます。アプリケーションが UDT を取り出すために getObject メソッドを呼び出すと、ドライバは接続に関連付けられている型マップがその UDT のエントリを持っているかどうか検査します。エントリが存在している場合、ドライバはその型マップを使ってカスタムマッピングします。 一致するエントリが存在しない場合、ドライバは標準的なマッピングを使います。

ほとんどのカスタムマッピングは、接続の型マップを使って行われます。しかしながら、ドライバに別の型マップを使わせることもできます。カスタムマッピングが可能なメソッドは、 2 つのバージョンを持ちます。 型マップをパラメータとして取るメソッドと、取らないメソッドです。通常は、型マップが指定されないので、ドライバは接続の型マップをデフォルトで使います。メソッドに型マップが渡された場合は、その型マップが接続の型マップより優先され、ドライバは、接続に関連付けられた型マップの代わりに、この型マップを使って UDT をマッピングします。渡された型マップに UDT のエントリが存在しない場合は、ドライバは、標準的なマッピングを行います。

setObject メソッドは、パラメータとして型マップを取らないので、少し異なる動作になります。setObjectSQLData インタフェースを実装しているクラスのインスタンス (つまり取り出されたときにカスタムマッピングされていたオブジェクト) が渡された場合は、ドライバには、このオブジェクトをマッピングするための機構がすでに設定されています。ドライバは、データベースへ送信する前に、UDT をその SQL の型にマッピングして、クラスインスタンスを変換します。setObject メソッドによって設定されるパラメータが、カスタムマッピングされていない場合は、ドライバは、データベースへ送信する前に、標準的なマッピングを使って変換します。

getObject および setObject メソッドだけが、 SQL 構造化型の取り出しおよび保存に使うことができるという事実が次のことを確かにします。 1 つだけある場合にカスタムマッピングが使用される。Array インタフェースの 4 つのメソッドに、型マップを渡す場合もあり、ARRAY の要素が UDT の場合は、その要素がクライアント上で実体化される時点でカスタムマッピングさせることができます。Struct メソッドの getAttributes にも、型マップを取るバージョンがあります。 この型マップは、クライアントへの送信前に SQL 構造化型の属性をカスタムマッピングするのに使われます。

8.7 動的データアクセス

ユーザがアクセスしたいのは、コンパイル時にそのデータ型がわかっている結果またはパラメータである場合がほとんどです。ただし、汎用的なブラウザまたはクエリーツールなどのアプリケーションは、アクセスするデータベーススキーマを知らない状態でコンパイルされます。このため、JDBC では静的データアクセスに加えて、完全に動的なデータアクセスもサポートしています。

3 つのメソッドが、コンパイル時にデータ型が不明な値にアクセスするのを支援します。

たとえば、アプリケーションが、さまざまな型を ResultSet オブジェクトの中で結果として受け付けることを可能にしたい場合には、ResultSet.getObject を使用することができます。

ResultSet.getObjectCallableStatement.getObject の両メソッドは Java Object として値を取り出します。Object は、すべての Java オブジェクトに対する基底クラスなので、任意の Java クラスのインスタンスは Object のインスタンスとして取り出すことが可能です。ただし、Java の型 booleancharbyteshortintlongfloat、および double は、組み込み型の「プリミティブ」型なので、Object クラスのインスタンスにはなりません。結果として、上記の型は、getObject メソッドでは取り出すことができません。ただし、それらのプリミティブ型には、ラッパーとして稼動する、それぞれに対応するクラスがあります。それらのクラスのインスタンスはオブジェクトであり、すなわち ResultSet.getObjectCallableStatement.getObject の両メソッドによって取り出すことが可能になります。表 8.3 は、JDBC 型から Java Object 型へのマッピングを示します。この表は、JDBC 型から Java 型への標準的なマッピングとは異なり、各プリミティブの Java 型はそのラッパークラスが取って代わります。 ただし、JDBC TINYINT と JDBC SMALLINT は、Java クラスの Integer にマッピングされます。

8.8 データベースへの Java オブジェクトの保存

JDBC 2.0 コア API への機能追加によって、Java オブジェクトをデータベースへより簡単に保存できるようになりました。JDBC 1.0 API の PreparedStatement.setObject メソッドは、Java プログラミング言語で定義されたオブジェクトの永続的な保存をすでにサポートしています。新しいデータ型 JAVA_OBJECT と新しいメソッド DatabaseMetaData.getUDTs により、データベース内に保存された Java オブジェクトの追跡がより簡単になりました。

8.9 型のマッピング一覧

この節には、JDBC と Java のデータの型に関連する以下の表を記載します。

表 8.1-Java の型にマッピングされる JDBC の型

この表は、JDBC の型と Java の型の概念上の対応を示しています。プログラマはこのマッピングを心に留めて、コードを記述する必要があります。たとえば、データベース内の値が SMALLINT の場合、JDBC アプリケーションで使うデータ型は、short になります。
getObject 以外のすべての CallableStatement.getXXX メソッドは、このマッピングを使います。CallableStatement および ResultSet インタフェースの getObject メソッドは、「Java のオブジェクト型にマッピングされる JDBC の型」のマッピングを使います。

表 8.2-JDBC の型にマッピングされる Java の型

この表は、ResultSet.updateXXX メソッドおよび IN パラメータに対してドライバが使うマッピングを示しています。PreparedStatement.setXXX メソッドおよび RowSet.setXXX メソッドは、この表を使って Java の型である IN パラメータを、データベースに送信するための JDBC の型にマッピングします。これらの 2 つのインタフェースの setObject メソッドは、「JDBC の型にマッピングされる Java のオブジェクト型」 のマッピングを使います。

表 8.3-Java のオブジェクト型にマッピングされる JDBC の型

この表は、ResultSet.getObject および CallableStatement.getObject の標準的なマッピングで使われます。

表 8.4-JDBC の型にマッピングされる Java のオブジェクト型

ターゲットの JDBC の型を示すパラメータが指定されない場合は、PreparedStatement.setObject および RowSet.setObject では、この表のマッピングが使われます。

表 8.5-setObject による Java のオブジェクト型から JDBC の型への変換

この表は、ターゲット側の JDBC の型として、どの JDBC 型を、 PreparedStatement.setObject および RowSet.setObject メソッドに指定できるか示しています。

表 8.6-ResultSet.getXXX メソッドによってサポートされている型変換

この表は、ResultSet.getXXX メソッドによって返される JDBC の型を示しています。太字の X は、JDBC の型の取り出しに推奨されるメソッドです。普通の x は、getXXX メソッドを使うことができる JDBC の型を示しています。
この表は、SQLInput.readXXX メソッドで使われる変換 (推奨されている変換を使うだけのものは除く) も含んでいます。

表 8.7-データベース固有の SQL の型にマッピングされる JDBC の型

この表は、個々のデータベースによって使われている、JDBC の型にもっとも近いデータ型の名前を示します。

8.9.1 Java の型にマッピングされる JDBC の型

JDBC の型


Java の型


CHAR


String


VARCHAR


String


LONGVARCHAR


String


NUMERIC


java.math.BigDecimal


DECIMAL


java.math.BigDecimal


BIT


boolean


TINYINT


byte


SMALLINT


short


INTEGER


int


BIGINT


long


REAL


float


FLOAT


double


DOUBLE


double


BINARY


byte[]


VARBINARY


byte[]


LONGVARBINARY


byte[]


DATE


java.sql.Date


TIME


java.sql.Time


TIMESTAMP


java.sql.Timestamp


CLOB


Clob


BLOB


Blob


ARRAY


Array


DISTINCT


基の型のマッピング


STRUCT


Struct


REF


Ref


JAVA_OBJECT


基の Java クラス

この表には、2 つの目的があります。まず、この表は、Java プログラミング言語の型と SQL の型との間の一般的な対応を示しています。また、CallableStatement.getXXX メソッドおよび SQLInput.readXXX メソッドによって使われるマッピングも表しています。CallableStatement.getObject メソッドで使われるマッピングについては、表 8.3 を参照してください。


8.9.2 JDBC の型にマッピングされる Java の型

Java の型


JDBC の型


String


CHARVARCHAR、または LONGVARCHAR


java.math.BigDecimal


NUMERIC


boolean


BIT


byte


TINYINT


short


SMALLINT


int


INTEGER


long


BIGINT


float


REAL


double


DOUBLE


byte[]


BINARY、VARBINARY、または LONGVARBINARY


java.sql.Date


DATE


java.sql.Time


TIME


java.sql.Timestamp


TIMESTAMP


Clob


CLOB


Blob


BLOB


Array


ARRAY


Struct


STRUCT


Ref


REF


Java クラス


JAVA_OBJECT

この表は、IN パラメータが DBMS に送信される前に使われる変換を示しています。 PreparedStatement.setXXX および RowSet.setXXX メソッドによって使われます。この変換は、ResultSet.updateXXX メソッドおよび SQLOutput.writeXXX メソッドでも使われます。PreparedStatement.setObject および RowSet.setObject メソッドは、表 8.4 のマッピングを使います。

String に対するマッピングは通常 VARCHAR ですが、値が VARCHAR 値に対するドライバの限界を超えている場合は、LONGVARCHAR になります。byte[] の場合も同様で、VARCHAR 値に対するドライバの限界に応じて、VARBINARY または LONGVARBINARY 値にマッピングされます。ほとんどの場合、CHARVARCHAR の選択は重要ではありません。ドライバによって適切に選択されます。BINARYVARBINARY の選択も同様です。


8.9.3 Java のオブジェクト型にマッピングされる JDBC の型

JDBC の型


Java のオブジェクト型


CHAR


String


VARCHAR


String


LONGVARCHAR


String


NUMERIC


java.math.BigDecimal


DECIMAL


java.math.BigDecimal


BIT


Boolean


TINYINT


Integer


SMALLINT


Integer


INTEGER


Integer


BIGINT


Long


REAL


Float


FLOAT


Double


DOUBLE


Double


BINARY


byte[]


VARBINARY


byte[]


LONGVARBINARY


byte[]


DATE


java.sql.Date


TIME


java.sql.Time


TIMESTAMP


java.sql.Timestamp


DISTINCT


基の型のオブジェクト型


CLOB


Clob


BLOB


Blob


ARRAY


Array


STRUCT


Struct または SQLData


REF


Ref


JAVA_OBJECT


基の Java クラス

この表は、ResultSet.getObject および CallableStatement.getObject メソッドで使われる、JDBC の型から Java のオブジェクト型へのマッピングを示しています。


8.9.4 JDBC の型にマッピングされる Java のオブジェクト型

Java のオブジェクト型


JDBC の型


String


CHAR、VARCHAR、または LONGVARCHAR


java.math.BigDecimal


NUMERIC


Boolean


BIT


Integer


INTEGER


Long


BIGINT


Float


REAL


Double


DOUBLE


byte[]


BINARY、VARBINARY、または LONGVARBINARY


java.sql.Date


DATE


java.sql.Time


TIME


java.sql.Timestamp


TIMESTAMP


Clob


CLOB


Blob


BLOB


Array


ARRAY


Struct


STRUCT


Ref


REF


Java クラス


JAVA_OBJECT

この表は、ターゲットの JDBC の型を示すパラメータが指定されない場合に、PreparedStatement.setObject メソッドで使われるマッピングです。PreparedStatement.setObject メソッドに対して指定できる JDBC の型は、表 8.5 を参照してください。

String に対するマッピングは通常 VARCHAR ですが、値が VARCHAR 値に対するドライバの限界を超えている場合は、LONGVARCHAR になることに注意してください。byte[] の場合も同様で、VARCHAR 値に対するドライバの限界に応じて、VARBINARY または LONGVARBINARY になります。


8.9.5 setObject による変換

setObject による変換[D]

「x」は、その Java のオブジェクト型を対応する JDBC の型に変換できる場合があることを示しています。この表は、 PreparedStatement.setObject または RowSet.setObject メソッドに渡すターゲット側の JDBC の型を指定するパラメータの可能性を示しています。指定した値が無効な場合、いくつかの変換は実行時に失敗します。


8.9.6 ResultSet.getXXX メソッドによる変換

ResultSet.getXXX メソッドによる変換[D]
SQLInput.readXXX メソッドがサポートしているのは、推奨されている変換だけです。「x」は、そのメソッドが JDBC の型を「取り出せること」を意味します。X」は、そのメソッドが JDBC の型を取り出のに「推奨されていること」を意味します。


8.9.7 データベース固有の SQL の型にマッピングされる JDBC の型

データベースごとに、サポートされている SQL の型は大きく異なっています。表 8.7 は、主要なデータベースについて、JDBC の型にもっとも近いデータベース固有の SQL の型を示しています。データベース固有の型の名前の存在は、「データベース固有の型が追加のセマンティクスを提供している場合があるにもかかわらず、対応している JDBC の型のセマンティクスを達成するのに、その与えられた型が使用されます。」を示しています。

注意:

  1. 一部のデータベースでは、いくつかの整数型および浮動小数点型に対してより多い精度が使われます。
  2. 一部のデータベースでは、DATE または TIME、あるいはその両方を含めることができる DATE 型または DATETIME 型が提供されます。
  3. VARCHARVARCHAR2 は、Oracle8 ではシノニムです。
  4. LONGVARCHAR に対して DB2 は、2G バイトの制限とともに「CLOB(n)」もサポートしています。
  5. LONGVARBINARY に対して、DB2 は、2G バイトの制限とともに「BLOB(n)」もサポートしています。
  6. SQL 文における BINARYVARBINARY、および LONGVARBINARY リテラルの扱いは、データベースによって大きく異なります。移植可能な方法で値を設定するのに、PreparedStatement.setBytes を使うことをお勧めします。
  7. SQL 文における DATETIME、および TIMESTAMP リテラルの扱いは、データベースによって大きく異なります。移植可能な方法で DateTime、および Timestamp の値を設定を設定するのに、JDBC SQL エスケープ構文を使うことをお勧めします。(「Statement 内の SQL エスケープ構文」を参照)

データベース固有の SQL の型にマッピングされる型[D]

前のイメージの詳しい説明を参照



[目次] [前の項目] [次の項目]

Copyright © 1999, Sun Microsystems, Inc. All rights reserved.