注: この章の内容は、Addison Wesley 社より Java シリーズの 1 巻として出版された『JDBCTM API Tutorial and Reference, Second Edition: Universal Data Access for the JavaTM 2 Platform』(ISBN 0-201-43328-1) に基づいて作成したものです。
SQL のデータ型は、Java プログラミング言語のデータ型と同一ではないので、Java の型を使用するアプリケーションと SQL の型を使用するデータベースの間で、データを移し変えるための機構が必要です。(本文中で使われている「Java の型」という表現は、「Java プログラミング言語上の型」を表しています。)
Java プログラミング言語で記述されているアプリケーションとデータベースの間でデータを移し換えるために、JDBC API では、次の 3 つのメソッドを提供しています。
SELECT
の結果を Java の型として取得するための ResultSet
クラスのメソッド
PreparedStatement
クラスのメソッド
OUT
パラメータを Java の型として取得するための CallableStatement
クラスのメソッド
この節では、さまざまなクラスやインタフェースに影響するデータ型についての情報をまとめ、SQL の型と Java の型の間のマッピングを示す一覧を参照しやすいように 1 箇所に置きました。また、SQL3 の型を含む、個々の汎用的な SQL のデータ型についても説明します。
異なるデータベース製品がサポートする 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 つ目は、INTEGER
、NUMERIC
、または 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
型のどれにも厳密には一致しませんが、CHAR
、VARCHAR
、または LONGVARCHAR
を正常に表現するのに十分な型情報を与えます。
ここでは、JDBC 1.0 と 2.0 API の両方でサポートされている JDBC のデータ型について説明します。また、標準の SQL の型および Java プログラミング言語の型との関係についても説明します。JDBC 2.0 コア API で導入された新しい JDBC のデータ型は、8.4 節で説明します。
JDBC の型 CHAR
、VARCHAR
、および 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 のプログラマは、CHAR
、VARCHAR
、および LONGVARCHAR
の JDBC 文字列の 3 つの型を区別する必要がありません。それぞれは Java の String
として表現することができ、必要とされた正確なデータ型を知らなくても SQL 文を正しく読み書きすることができます。
CHAR
、VARCHAR
、および LONGVARCHAR
は、String
または char[]
のどれにでもマッピングすることができますが、String
の方が通常の使用のためにはより適切です。また、String
クラスにより、String
と char[]
の間の変換がより簡単になります。String
オブジェクトを char[]
に変換するメソッドがあり、また char[]
を String
オブジェクトに調整するコンストラクタもあります。
対処すべき問題の 1 つは、CHAR(n)
型の固定長 SQL 文字列をどのように処理するかです。最適なのは、JDBC ドライバ (または DBMS) が空白で適切なパディングを行うことです。したがって CHAR(n)
フィールドがデータベースから取り出されたとき、ドライバがそれを長さが n
の Java の String
オブジェクトに変換しますが、これには末尾にパディングの空白がいくつか含まれている可能性があります。これとは逆に、String
オブジェクトが CHAR(n)
フィールドに送信されると、ドライバまたはデータベース、あるいはその両方が必要なパディング用空白を文字列の末尾に追加し、その長さを n
にします。
メソッド ResultSet.getString
は、新しい String
オブジェクトを割り当てたり返したりしますが、データを CHAR
、VARCHAR
、および LONGVARCHAR
フィールドから取り出すことをお勧めします。これは通常のデータを取り出すには適切ですが、JDBC の LONGVARCHAR
型を使用して何メガバイトかの文字列を保存する場合は扱いにくいことがあります。このようなケースを処理するために、ResultSet
インタフェースの 2 つのメソッドにより、プログラマが LONGVARCHAR
の値を任意のサイズの塊でシーケンシャルにデータを読み取れる Java 入力ストリームとして取り出すことができるようにしています。これらのメソッドは getAsciiStream
と getCharacterStream
で、LONGVARCHAR
列に保存されているデータを ASCII または Unicode 文字のストリームとして配信します。メソッド getUnicodeStream
は、推奨されていません。
後述する SQL3 の CLOB
データ型を使って、大量の文字データを表すこともできます。
JDBC の型 BINARY
、VARBINARY
、および 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 の型」の表を参照してください。
BINARY
、VARBINARY
、および LONGVARBINARY
は、Java プログラミング言語では byte
配列として、すべて同じように表現できます。要求されている BINARY
データ型を正確に知らなくても、SQL 文を正しく読み書きできるため、Java プログラミング言語でコードを記述するプログラマがそれらの型を区別する必要はありません。
BINARY
や VARBINARY
の値を取り出すために推奨されるメソッドは、ResultSet.getBytes
です。JDBC LONGVARBINARY
型の列が、何メガバイト長ものバイト配列を保存している場合には、getBinaryStream
メソッドをお勧めします。LONGVARCHAR
の場合と同様に、このメソッドはプログラマが LONGVARBINARY
値を、あとでより小さな塊で読むことができる Java 入力ストリームとして取り出すことを可能にします。
後述する SQL3 の BLOB
データ型を使って、大量のバイナリデータを表すこともできます。
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
型とするものです。
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
値を保持することができます。
JDBC 型の SMALLINT
は、-32768 から 32767 までの 16 ビットの符号付き整数値を表します。
対応する SQL の型の SMALLINT
は SQL-92 で定義され、すべての主要なデータベースによってサポートされています。SQL-92 標準では、SMALLINT
の精度を実装に任せていますが、実際には、すべての主要なデータベースは少なくとも 16 ビットをサポートしています。
JDBC SMALLINT
型に対して推奨される Java マッピングは、Java short
とするものです。
JDBC 型の INTEGER
は、-2147483648 から 2147483647 までの 32 ビットの符号付き整数値を表します。
対応する SQL の型の INTEGER
は SQL-92 で定義され、すべての主要なデータベースによって広くサポートされています。SQL-92 標準では、INTEGER
の精度を実装に任せていますが、実際には、すべての主要なデータベースは少なくとも 32 ビットをサポートしています。
JDBC INTEGER
型に対して推奨される Java マッピングは、Java int
とするものです。
JDBC 型の BIGINT
は、-9223372036854775808
から 9223372036854775807
までの 64 ビットの符号付き整数値を表します。
対応する SQL の型の BIGINT
は、SQL の非標準の拡張です。実際、SQL BIGINT
型は、主要などのデータベースにも現在実装されていません。 したがって、移植性を望むコードでは、使用を避けることをお勧めします。
JDBC BIGINT
型に対して推奨される Java マッピングは、Java long
とするものです。
JDBC 型の REAL
は、7 桁の仮数部をサポートする「単精度」の浮動小数点数です。
対応する SQL の型の REAL
は SQL-92 で定義され、すべての主要なデータベースによって、一般的ではありませんが、広くサポートされています。SQL-92 標準では、REAL
の精度を実装に任せていますが、実際には、REAL
をサポートするすべての主要なデータベースは少なくとも 7 桁の仮数精度をサポートしています。
JDBC REAL
型に対して推奨される Java マッピングは、Java float
とするものです。
JDBC 型の DOUBLE
は、15 桁の仮数部をサポートする「倍精度」の浮動小数点数です。
対応する SQL の型は、DOUBLE
PRECISION
であり SQL-92 で定義され、すべての主要なデータベースによって、広くサポートされています。SQL-92 標準では、DOUBLE
PRECISION
の精度を実装に任せていますが、実際には、DOUBLE
PRECISION
の精度をサポートするすべての主要なデータベースは少なくとも 15 桁の仮数精度をサポートしています。
JDBC DOUBLE
型に対して推奨される Java マッピングは、Java double
とするものです。
JDBC 型の FLOAT
は、基本的には JDBC 型の DOUBLE
と等価です。FLOAT
と DOUBLE
の両方を提供したのは、以前のデータベースの API との一貫性を維持しようとしたためですが、混乱を起こす危険性が考えられます。FLOAT
は、15 桁の仮数部をサポートする「倍精度」浮動小数点数です。
対応する SQL の型の FLOAT
は SQL-92 で定義されています。SQL-92 標準では、
FLOAT
の精度を実装に任せていますが、実際には、FLOAT
をサポートするすべての主要なデータベースは少なくとも 15 桁の仮数精度をサポートしています。
この FLOAT
型に対して推奨される Java マッピングは、Java double
とするものです。ただし、倍精度の SQL FLOAT
と単精度の Java float
との間の混乱が予想されるため、JDBC プログラマは、通常の場合には、FLOAT
よりも JDBC DOUBLE
型を使用することをお勧めします。
JDBC 型の DECIMAL
と NUMERIC
は非常に似ています。両方とも、固定精度の 10 進数を表します。 対応する SQL の型 DECIMAL
および NUMERIC
は、SQL-92 で定義されており、広範囲に実装されています。これらの SQL の型は、精度とスケールのパラメータを取ります。精度は、サポートされている 10 進数の総桁数で、スケールは、小数点以下の桁数です。ほとんどの DBMS では、スケールは精度以下です。たとえば、「12.345」の精度は 5 であり、スケールは 3 となります。 「.11」の精度は 2 であり、スケールは 2 となります。 JDBC は、すべての DECIMAL
の型 と NUMERIC
の型が、少なくとも 15 桁の精度とスケールをサポートすることを要求しています。
DECIMAL
と NUMERIC
の唯一の相違は、SQL-92 仕様が、NUMERIC
型が正確に指定の精度で表現されることを要求する一方で、DECIMAL
型では、型の生成時に指定された精度を超えた精度を追加することを実装に許している点にあります。したがって、型の NUMERIC(12,4)
で作成された列は、常に正確に 12 桁で表され、型の DECIMAL(12,4)
で作成された列は、より大きな桁数で表されることもあります。
DECIMAL
型と NUMERIC
型に推奨される Java マッピングは、java.math.BigDecimal
です。java.math.BigDecimal
型は、BigDecimal
型をほかの BigDecimal
型、整数型、および浮動少数点型と加減乗除できる、算術計算を提供しています。
DECIMAL
と NUMERIC
の値を取り出すために推奨されるメソッドは、ResultSet.getBigDecimal
です。JDBC は、単純な Strings
または char
の配列としてこれらの SQL の型へのアクセスも可能にします。したがって、Java プログラマは、getString
を使用して NUMERIC
または DECIMAL
の結果を受け取ることができます。ただし、これにより、アプリケーションの作成者が文字列上で算術計算をすることが必要になるので、DECIMAL
または NUMERIC
を通貨の値として使用する一般的な場合が、むしろ扱いにくくなります。これらの SQL の型を Java の数値型のどれかとして取り出すことも可能です。
時間に関連する JDBC の型には以下の 3 つがあります。
DATE
型は日付、月、および年で構成される。対応する SQL DATE
型は SQL-92 で定義されているが、主要なデータベースの一部しかサポートしていない。データベースの中には、同じような意味を持つ代替えの SQL の型を提供するものもある
TIME
型は、時、分、秒で構成される。対応する SQL TIME
型は SQL-92 で定義されているが、主要なデータベースの一部しかサポートしていない。DATE
の場合のように、データベースの中には、同じような意味を持つ代替えの SQL の型を提供するものもある
TIMESTAMP
型は DATE
と TIME
とナノ秒フィールドを表す。対応する SQL TIMESTAMP
型は SQL-92 で定義されているが、非常に少数のデータベースが実装するだけである
標準の Java クラス java.util.Date
は、これら 3 つの JDBC の型に正確には一致していないので (標準の Java クラスには DATE
と TIME
の情報はあるが、ナノ秒はない)、JDBC は java.util.Date
の 3 つのサブクラスを定義して、SQL の型に対応しています。これらは以下のとおりです。
DATE
情報のための java.sql.Date
。java.util.Date
基底クラスの時、分、秒、およびミリ秒フィールドにはゼロが設定される必要がある。java.sql.Date
コンストラクタに提供されるミリ秒が負の数の場合は、ドライバはその日付を 1970 年 1 月 1 日以前のミリ秒数として計算する。
負でない場合は、その日付を 1970 年 1 月 1 日以後のミリ秒数として計算する
TIME
情報のための java.sql.Time
。java.util.Date
基底クラスの年、月、日フィールドは、1970 年 1 月 1 日に設定される。Java の暦ではこれが「ゼロ」日になる
TIMESTAMP
情報のための java.sql.Timestamp
。java.util.Date
クラスの継承。ナノ秒フィールドが追加されている
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
オブジェクトに組み込まれています。 このオブジェクトは、Date
、Time
、および Timestamp
の値の取得および設定に使われる新しいバージョンのメソッドに渡されます。タイムゾーンが指定されていない場合は、日付、時刻、およびタイムスタンプの計算時にアプリケーションが動作している Virtual Machine のタイムゾーンが使われます。
ISO (International Organization for Standardization) および IEC (International Electrotechnical Commission) は、一般的に SQL3 の型と呼ばれる新しいデータ型を定義しています。これらの新しい SQL3 のデータ型のうち、BLOB
、CLOB
、ARRAY
、および 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
メソッドを使って、取り出したり、保存したりすることができます。
JDBC の型 BLOB
は、SQL3 の BLOB
(Binary Large Object) を表します。
JDBC BLOB
の値は、Java プログラミング言語の Blob
インタフェースのインスタンスにマッピングされます。ドライバが標準的な実装に準拠している場合、Blob
オブジェクトは、オブジェクトのバイナリデータを含まず、サーバ上の BLOB 値を論理的に指すので、効率が大きく向上します。Blob
インタフェースは、必要な時に BLOB
データをクライアント上に実体化するためのメソッドを提供しています。
JDBC の型 CLOB
は、SQL3 の型 CLOB
(Character Large Object) を表します。
JDBC CLOB
の値は、Java プログラミング言語の Clob
インタフェースのインスタンスにマッピングされます。ドライバが標準的な実装に準拠している場合、Clob
オブジェクトは、オブジェクトの文字データを含まず、サーバ上の CLOB
値を論理的に指すので、効率が大きく向上します。Clob
インタフェースの 2 つのメソッドによって、 CLOB
オブジェクトのデータがクライアント上に実体化されます。
JDBC の型 ARRAY
は、SQL3 の型 ARRAY
を表します。
ARRAY
の値は、Java プログラミング言語の Array
インタフェースのインスタンスにマッピングされます。ドライバが標準的な実装に準拠している場合は、ARRAY
オブジェクトは、オブジェクトの要素を含まず、サーバ上の ARRAY
値を論理的に指すので、効率が大きく向上します。Array
インタフェースには、クライアント上の ARRAY
オブジェクトの要素を、配列または ResultSet
オブジェクトの形式で実体化するメソッドが含まれています。
JDBC の型 DISTINCT
は、SQL3 の型 DISTINCT
を表します。
標準的なマッピングでは、DISTINCT
型は、DISTINCT
オブジェクトの基底型がマッピングされる Java の型にマッピングされます。たとえば、基底型が CHAR
の DISTINCT
型は、String
オブジェクトにマッピングされます。 基底型が SQL INTEGER
の DISTINCT
型は、int
にマッピングされます。
DISTINCT
型は、Java プログラミング言語にカスタムマッピングすることもできます。カスタムマッピングは、SQLData
インタフェースを実装するクラス、および、java.util.Map
オブジェクト内のエントリで構成されます。
JDBC の型 STRUCT
は、SQL3 の構造化型を表します。SQL の構造化型は、ユーザによって CREATE TYPE
文を使って定義される、1 つ以上の属性で構成される型です。これらの属性には、組み込み済みまたはユーザ定義の任意の SQL のデータ型を指定できます。
標準的なマッピングでは、SQL の型 STRUCT
は、Java プログラミング言語の Struct
オブジェクトにマッピングされます。Struct
オブジェクトには、オブジェクトが表す STRUCT
値の各属性の値が含まれます。
STRUCT
値は、Java プログラミング言語のクラスにカスタムマッピングできます。STRUCT
内の各属性は、クラスのフィールドにマッピングできます。カスタムマッピングは、SQLData
インタフェースを実装するクラス、および、 java.util.Map
オブジェクト内のエントリで構成されます。
JDBC の型 REF
は、SQL3 の型 REF
を表します。SQL REF
は、SQL の構造化型のインスタンスを参照、つまり論理的に指します。 このインスタンスは、REF
によって持続的および一意に識別されます。Java プログラミング言語では、Ref
インタフェースは SQL REF
を表します。
クライアント上に実体化した属性値を持たないようにして、アプリケーションからデータベースの SQL 構造化型のインスタンスを指すようにしたい場合には、REF
型、つまり SQL 構造化型への参照を使うことができます。
REF
値は、SQL 構造化型の特定のインスタンスに対して特別に作成された一意の識別子です。この値は、値が参照するインスタンスとともに、サーバ上の特定のテーブルに永続的に保存されます。アプリケーションからその特別なテーブルの REF
値を select すれば、その REF
値によって識別される構造化型のインスタンスの代わりに、その REF
値を使用できます。
JDBC 2.0 コア API に追加された JDBC 型の JAVA_OBJECT
を使うと、Java プログラミング言語のオブジェクトを、データベースの値として簡単に使うことができます。JAVA_OBJECT
は、Java プログラミング言語で定義されているクラスのインスタンスに対する単なる型のコードで、データベースのオブジェクトとして保存されます。JAVA_OBJECT
型は、Java オブジェクトを直接保存できるように型システムが拡張されているデータベースで使われます。JAVA_OBJECT
の値は、直列化された Java オブジェクトとして、またはベンダー固有の形式で保存されます。
JAVA_OBJECT
型は、getTypeInfo
、getColumns
、getUDTs
などを含めた、さまざまな 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
型にナロー変換する必要があります。
Java プログラミング言語で記述されたプログラムがデータをデータベースから取り出すという状況では、必ずなんらかの形でマッピングとデータ変換が必要です。ほとんどの場合、JDBC API を使うプログラマは、自分が対象としているデータベースの仕組みについての知識を持っています。つまり、データベースにどのようなテーブルがあり、それらのテーブルの各列のデータ型が何かなどについて知っています。したがって、ResultSet
、PreparedStatement
、および CallableStatement
のインタフェースで固定的に入力したアクセスメソッドを使用することができます。この節では、3 つの異なるシナリオを示すとともに、それぞれの場合に必要なデータのマッピングと変換について説明します。
もっとも一般的なケースでは、ユーザが簡単な SQL 文を実行して、結果を持つ ResultSet
オブジェクトを受け取ります。データベースが返し、ResultSet
列に保存される各値は、JDBC のデータ型を持っています。ResultSet.getXXX
メソッドへの呼び出しは、その値を Java のデータ型として取り出します。たとえば、ResultSet
列に JDBC の FLOAT
値が入っている場合、メソッド getDouble
はその値を Java の double
として取り出します。表 8.6 は、どの getXXX
メソッドがどの JDBC の型を取り出すために使用されるかを示します。ResultSet
列の型がわからないユーザは、メソッド ResultSet.getMetaData
を呼び出してから、ResultSetMetaData.getColumnType
を呼び出すことにより、その情報を取得することができます。
もう 1 つのシナリオでは、ユーザが入力パラメータを取る SQL クエリーを送信します。この場合、ユーザは PreparedStatement.setXXX
メソッドを呼び出して、各入力パラメータに値を割り当てます。たとえば、PreparedStatement.setLong(1, 2345678)
は、最初のパラメータに 2345678
という値を Java の long
として割り当てます。ドライバは、データベースに送信するために、2345678
を JDBC の BIGINT
に変換します。ドライバがデータベースにどの JDBC の型を送信するかは、Java の型から JDBC の型への標準的なマッピングによって決定されます。 この標準的なマッピングは、表 8.2 に示されています。
さらにもう 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 byte
で 25
を設定します。ドライバは、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.getXXX
と CallableStatement.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 を参照)。ドライバは、パラメータをデータベースに送信する前に、明示的または暗黙的な変換を行います。
SQL3 のユーザ定義型 (UDT) である構造化型および DISTINCT
型は、Java プログラミング言語のクラスにカスタムマッピングできます。カスタムマッピングが設定されている場合、ドライバは UDT に対して JDBC と Java 間の型変換を行うときに、標準的なマッピングの代わりにカスタムマッピングを使います。
UDT は、ResultSet.getObject
および CallableStatement.getObject
メソッドを使ってデータベースから取り出され、PreparedStatement.setObject
メソッドを使って元のデータベースに返信されます。アプリケーションが UDT を取り出すために getObject
メソッドを呼び出すと、ドライバは接続に関連付けられている型マップがその UDT のエントリを持っているかどうか検査します。エントリが存在している場合、ドライバはその型マップを使ってカスタムマッピングします。 一致するエントリが存在しない場合、ドライバは標準的なマッピングを使います。
ほとんどのカスタムマッピングは、接続の型マップを使って行われます。しかしながら、ドライバに別の型マップを使わせることもできます。カスタムマッピングが可能なメソッドは、 2 つのバージョンを持ちます。 型マップをパラメータとして取るメソッドと、取らないメソッドです。通常は、型マップが指定されないので、ドライバは接続の型マップをデフォルトで使います。メソッドに型マップが渡された場合は、その型マップが接続の型マップより優先され、ドライバは、接続に関連付けられた型マップの代わりに、この型マップを使って UDT をマッピングします。渡された型マップに UDT のエントリが存在しない場合は、ドライバは、標準的なマッピングを行います。
setObject
メソッドは、パラメータとして型マップを取らないので、少し異なる動作になります。setObject
に SQLData
インタフェースを実装しているクラスのインスタンス (つまり取り出されたときにカスタムマッピングされていたオブジェクト) が渡された場合は、ドライバには、このオブジェクトをマッピングするための機構がすでに設定されています。ドライバは、データベースへ送信する前に、UDT をその SQL の型にマッピングして、クラスインスタンスを変換します。setObject
メソッドによって設定されるパラメータが、カスタムマッピングされていない場合は、ドライバは、データベースへ送信する前に、標準的なマッピングを使って変換します。
getObject
および setObject
メソッドだけが、 SQL 構造化型の取り出しおよび保存に使うことができるという事実が次のことを確かにします。 1 つだけある場合にカスタムマッピングが使用される。Array
インタフェースの 4 つのメソッドに、型マップを渡す場合もあり、ARRAY
の要素が UDT の場合は、その要素がクライアント上で実体化される時点でカスタムマッピングさせることができます。Struct
メソッドの getAttributes
にも、型マップを取るバージョンがあります。 この型マップは、クライアントへの送信前に SQL 構造化型の属性をカスタムマッピングするのに使われます。
ユーザがアクセスしたいのは、コンパイル時にそのデータ型がわかっている結果またはパラメータである場合がほとんどです。ただし、汎用的なブラウザまたはクエリーツールなどのアプリケーションは、アクセスするデータベーススキーマを知らない状態でコンパイルされます。このため、JDBC では静的データアクセスに加えて、完全に動的なデータアクセスもサポートしています。
3 つのメソッドが、コンパイル時にデータ型が不明な値にアクセスするのを支援します。
たとえば、アプリケーションが、さまざまな型を ResultSet
オブジェクトの中で結果として受け付けることを可能にしたい場合には、ResultSet.getObject
を使用することができます。
ResultSet.getObject
と CallableStatement.getObject
の両メソッドは Java Object
として値を取り出します。Object
は、すべての Java オブジェクトに対する基底クラスなので、任意の Java クラスのインスタンスは Object
のインスタンスとして取り出すことが可能です。ただし、Java の型 boolean
、char
、byte
、short
、int
、long
、float
、および double
は、組み込み型の「プリミティブ」型なので、Object
クラスのインスタンスにはなりません。結果として、上記の型は、getObject
メソッドでは取り出すことができません。ただし、それらのプリミティブ型には、ラッパーとして稼動する、それぞれに対応するクラスがあります。それらのクラスのインスタンスはオブジェクトであり、すなわち ResultSet.getObject
と CallableStatement.getObject
の両メソッドによって取り出すことが可能になります。表 8.3 は、JDBC 型から Java Object
型へのマッピングを示します。この表は、JDBC 型から Java 型への標準的なマッピングとは異なり、各プリミティブの Java 型はそのラッパークラスが取って代わります。 ただし、JDBC TINYINT
と JDBC SMALLINT
は、Java クラスの Integer
にマッピングされます。
JDBC 2.0 コア API への機能追加によって、Java オブジェクトをデータベースへより簡単に保存できるようになりました。JDBC 1.0 API の PreparedStatement.setObject
メソッドは、Java プログラミング言語で定義されたオブジェクトの永続的な保存をすでにサポートしています。新しいデータ型 JAVA_OBJECT
と新しいメソッド DatabaseMetaData.getUDTs
により、データベース内に保存された Java オブジェクトの追跡がより簡単になりました。
この節には、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 の型にもっとも近いデータ型の名前を示します。
この表には、2 つの目的があります。まず、この表は、Java プログラミング言語の型と SQL の型との間の一般的な対応を示しています。また、CallableStatement.getXXX
メソッドおよび SQLInput.readXXX
メソッドによって使われるマッピングも表しています。CallableStatement.getObject
メソッドで使われるマッピングについては、表 8.3 を参照してください。
この表は、IN パラメータが DBMS に送信される前に使われる変換を示しています。 PreparedStatement.setXXX
および RowSet.setXXX
メソッドによって使われます。この変換は、
ResultSet.updateXXX
メソッドおよび SQLOutput.writeXXX
メソッドでも使われます。PreparedStatement.setObject
および RowSet.setObject
メソッドは、表 8.4 のマッピングを使います。
String
に対するマッピングは通常 VARCHAR
ですが、値が VARCHAR
値に対するドライバの限界を超えている場合は、LONGVARCHAR
になります。byte[]
の場合も同様で、VARCHAR
値に対するドライバの限界に応じて、VARBINARY
または LONGVARBINARY
値にマッピングされます。ほとんどの場合、CHAR
と VARCHAR
の選択は重要ではありません。ドライバによって適切に選択されます。BINARY
と VARBINARY
の選択も同様です。
この表は、ResultSet.getObject
および CallableStatement.getObject
メソッドで使われる、JDBC の型から Java のオブジェクト型へのマッピングを示しています。
この表は、ターゲットの JDBC の型を示すパラメータが指定されない場合に、PreparedStatement.setObject
メソッドで使われるマッピングです。PreparedStatement.setObject
メソッドに対して指定できる JDBC の型は、表 8.5 を参照してください。
String
に対するマッピングは通常 VARCHAR
ですが、値が VARCHAR
値に対するドライバの限界を超えている場合は、LONGVARCHAR
になることに注意してください。byte[]
の場合も同様で、VARCHAR
値に対するドライバの限界に応じて、VARBINARY
または LONGVARBINARY
になります。
「x」は、その Java のオブジェクト型を対応する JDBC の型に変換できる場合があることを示しています。この表は、 PreparedStatement.setObject
または RowSet.setObject
メソッドに渡すターゲット側の JDBC の型を指定するパラメータの可能性を示しています。指定した値が無効な場合、いくつかの変換は実行時に失敗します。
[D]
SQLInput.readXXX
メソッドがサポートしているのは、推奨されている変換だけです。「x」は、そのメソッドが JDBC の型を「取り出せること」を意味します。X」は、そのメソッドが JDBC の型を取り出のに「推奨されていること」を意味します。
データベースごとに、サポートされている SQL の型は大きく異なっています。表 8.7 は、主要なデータベースについて、JDBC の型にもっとも近いデータベース固有の SQL の型を示しています。データベース固有の型の名前の存在は、「データベース固有の型が追加のセマンティクスを提供している場合があるにもかかわらず、対応している JDBC の型のセマンティクスを達成するのに、その与えられた型が使用されます。」を示しています。
注意:
DATE
または TIME
、あるいはその両方を含めることができる DATE
型または DATETIME
型が提供されます。
VARCHAR
と VARCHAR2
は、Oracle8 ではシノニムです。
LONGVARCHAR
に対して DB2 は、2G バイトの制限とともに「CLOB(n)」もサポートしています。
LONGVARBINARY
に対して、DB2 は、2G バイトの制限とともに「BLOB(n)」もサポートしています。
BINARY
、VARBINARY
、および LONGVARBINARY
リテラルの扱いは、データベースによって大きく異なります。移植可能な方法で値を設定するのに、PreparedStatement.setBytes
を使うことをお勧めします。
DATE
、TIME
、および TIMESTAMP
リテラルの扱いは、データベースによって大きく異なります。移植可能な方法で Date
、Time
、および Timestamp
の値を設定を設定するのに、JDBC SQL エスケープ構文を使うことをお勧めします。(「Statement 内の SQL エスケープ構文」を参照)