JDBC インターフェイスの詳細¶
ここでは、Denodo の JDBC ドライバーに固有の情報について説明します。
ビューとそのフィールドの説明¶
このドライバーは、ビューとそのフィールドの説明を、テーブルまたはビュー、およびそのフィールドのメタデータの [REMARKS] 列に公開します。
blob 値のコンテンツタイプの取得¶
このドライバーは、blob フィールドの コンテンツタイプ を使用できるようにします。次の例に、その方法を示します。
ResultSet rs = stmt.executeQuery(...);
...
com.denodo.vdb.jdbcdriver.VDBJDBCBlob blob =
(com.denodo.vdb.jdbcdriver.VDBJDBCBlob) rs.getBlob(index);
String contentType = blob.getContentType();
Denodo JDBC ドライバーを使用した日時値の操作¶
以下のサブセクションでは、JDBC ドライバーを使用してさまざまな日時値を操作する方法について説明します。
準備済みステートメントのパラメータでの日時値の設定¶
次の表に、パラメータ (「?」) の値を設定するために呼び出す必要がある PreparedStatement クラスのメソッドを、値の型ごとに示します。
型 |
PreparedStatement でその型のパラメータを設定するためのメソッド |
---|---|
localdate |
以下のいずれか
例: // Creating a LocalDate object
setObject(1, java.time.LocalDate.of(2018,01,15));
// Creating a Date object
setDate(1, java.sql.Date.valueOf("2018-01-15"));
java.sql.Date クラスのドキュメントによれば、「setDate」を使用する場合、「Date」オブジェクトを「正規化」する必要があります。そのためには、インスタンスが関連付けられている特定のタイムゾーンの時間、分、秒、およびミリ秒を 0 に設定します。そのため、LocalDate オブジェクトの方が簡単に使用できます。 |
time |
以下のいずれか
例: // Creating a LocalTime object
setObject(1, LocalTime.of(11, 58, 59, 123000000));
// Creating a java.sql.Time object
setTime(2, java.sql.Time.valueOf("11:58:59"));
java.sql.Time クラスのドキュメントによれば、「setTime」を使用する場合、Time オブジェクトの日付コンポーネントを、1970 年 1 月 1 日の「ゼロエポック」値に設定する必要があります。そのため、LocalTime オブジェクトの方が簡単に使用できます。 |
timestamp |
PreparedStatement.setObject(java.time.LocalDateTime) 例: setObject(1, java.time.LocalDateTime.of(
2018, 01, 15, 23, 58, 59, 256000000))
最後のパラメータは、値がナノ秒単位であるため 256 ミリ秒を表します。Denodo では、timestamp、timestamptz、および time の最大精度はナノ秒ではなくミリ秒です。 PreparedStatement.setTimeStamp() を使用する場合は、timestamptz 値を指定する必要があります。そうしないと、クエリに timestamptz から timestamp へのキャストがある場合を除き、クエリは失敗します。また、キャストがある場合でも、それが機能するには、コネクション URI のパラメータ i18n が Denodo サーバーの i18n 設定と一致する必要があります。 |
date (非推奨) |
timestamptz と同じ (以下を参照) |
timestamptz |
以下のいずれか
例: // Creating an OffsetDateTime object
setObject(1, OffsetDateTime.parse(
"2018-01-01T21:15:00+01:00"))
// Creating a Timestamp object
SimpleDateFormat sdf =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss XXX");
sdf.setTimeZone("GMT");
setTimestamp(
1
, sdf.parse("1982-12-13 01:59:59 +0000"));
|
interval_year_month |
setObject(java.time.Period) 例: // Equivalent to INTERVAL '145-11' YEAR TO MONTH
setObject(1, Period.ofYears(145).plusMonths(11));
// Equivalent to INTERVAL '145' YEAR
setObject(Period.ofYears (145));
|
interval_day_second |
setObject(java.time.Duration) 例: // Equivalent to INTERVAL '4 5:12' DAY TO MINUTE
setObject(Duration.ofDays(4).plusHours(5).plusMinutes(12));
// Equivalent to INTERVAL '4 5:12:10.222' DAY TO SECOND
setObject(Duration.parse("P4DT5H12M10.222S"));
|
ドライバーが日時型と時間間隔型をレポートする方法¶
次の表に、JDBC ドライバーが各日時型をレポートする方法を示します。
Denodo の型名 |
ResultSetMetaData.getColumnTypeName() メソッドで返される型名 |
ResultSetMetaData.getColumnType(int) メソッドで返される値 |
---|---|---|
localdate |
DATE |
91 |
time |
TIME |
92 |
timestamp |
TIMESTAMP |
93 |
date (非推奨) |
TIMESTAMP_WITH_TIMEZONE |
2014 |
timestamptz |
TIMESTAMP_WITH_TIMEZONE |
2014 |
interval_year_month |
INTERVAL_YEAR_MONTH |
2020 |
interval_day_second |
INTERVAL_DAY_SECOND |
2021 |
date
型および timestamptz
型は同じ型 (TIMESTAMP WITH TIMEZONE) でレポートされるため、クライアントアプリケーションはこれらを区別できません。これは、Denodo 6.0 以前のバージョンから Denodo 8.0 へのアップグレードを容易にするための意図的な動作です。クライアントアプリケーションはこれらの型を区別する必要はなく、どちらも timestamptz
として扱います。
interval_year_month
型および interval_day_second
型は JDBC API に含まれていないため、それらのコードは Denodo によって定義されています。
Denodo の型名 |
ResultSetMetaData.getColumnClassName(int) の結果 |
ResultSet.getObject() クラスによって返されるオブジェクトの Java クラス |
---|---|---|
localdate |
java.sql.Date |
java.sql.Date |
time |
java.sql.Time |
java.sql.Time |
timestamp |
java.sql.Timestamp |
java.sql.Timestamp |
date (非推奨) |
java.sql.Timestamp |
java.sql.Timestamp |
timestamptz |
java.sql.Timestamp |
java.sql.Timestamp |
interval_year_month |
java.lang.Long |
java.lang.Long この値を ドライバーから |
interval_day_second |
java.lang.Long |
java.lang.Long この値を ドライバーから |
Struct (レジスター) 内部のエレメント名の取得¶
Denodo の JDBC ドライバーは、複合値を JDBC API のクラスに変換します。
register
型の値を java.sql.Struct オブジェクトに変換します。array
型の値を java.sql.Array オブジェクトに変換します。java.sql.Array
オブジェクトはStruct
オブジェクトの配列です。
標準の JDBC API では、 java.sql.Struct
オブジェクトの内部 (register
フィールドの内部など) にある値を取得するメソッドが提供されています。しかし、 Struct
のサブフィールドの名前を取得する方法や、サブフィールドの名前でこれらの値を取得する方法は提供されていません。
ここでは、Denodo JDBC ドライバーを使用して以下のことを行う方法について説明します。
Struct
オブジェクトのサブフィールドの名前を取得するサブフィールドの値を
register
の内部の位置ではなくその名前で取得する
たとえば、アプリケーションで last_name
サブフィールドと first_name
サブフィールドが含まれる register
フィールドを返すクエリを実行するとします。行ごとに、結果セットは Struct
オブジェクトを返します。各 Struct
オブジェクトの値を取得するには、アプリケーションは Struct.getAttributes()
メソッドを呼び出す必要があります。このメソッドは、姓と名の 2 つの値の配列を返します。その後、このレジスターを変更してサブフィールド (たとえば telephone
) を追加した場合、 Struct.getAttributes()
によって返される配列には、2 つではなく 3 つのエレメントが含まれます。また、配列の最初のエレメントが名ではなく電話番号になっている場合、アプリケーションは無効なデータを取得します。
こうした保全性の問題を回避するには、Denodo JDBC API のクラスを使用して Struct
の値をレジスター内の位置ではなく名前で取得します。そうすることで、変更に対するアプリケーションの堅牢性が向上します。
次の例に、これを行う方法を示します。
import com.denodo.vdb.jdbcdriver.printer.Field;
import com.denodo.vdb.jdbcdriver.VDBJDBCResultSetMetaData;
import com.denodo.vdb.vdbinterface.common.clientResult.vo.descriptions.type.RegisterVO;
import com.denodo.vdb.vdbinterface.common.clientResult.vo.descriptions.type.RegisterValueVO;
...
public static void main(String[] args)
throws Exception {
/*
* The method getConnection() returns a Connection to Virtual
* DataPort
*/
Connection connection = getConnection();
Statement st = connection.createStatement();
String query = "SELECT * FROM view_with_compound_fields";
ResultSet rs = st.executeQuery(query);
/*
* The classes 'VDBJDBCResultSetMetaData' and 'Field' are part
* of the Denodo JDBC API. They do not belong to the standard
* JDBC API.
*/
VDBJDBCResultSetMetaData metaData =
(VDBJDBCResultSetMetaData) rs.getMetaData();
Field[] fields = metaData.getFields();
while (rs.next()) {
int columnCount = metaData.getColumnCount();
for (int i = 1; i <= columnCount; i++) {
Object value = rs.getObject(i);
if (value != null) {
if (metaData.getColumnType(i) == Types.STRUCT) {
/*
* The JDBC API represents the values of type
* 'register' as 'Struct' objects.
*/
/*
* The classes 'RegisterVO' and
* 'RegisterValueVO' are part of the Denodo JDBC
* API. They do not belong to the standard Java
* API.
*/
RegisterVO vdpType =
((RegisterVO) fields[i - 1].getVdpType());
List<RegisterValueVO> registerSubTypes =
vdpType.getElements();
Struct struct = (Struct) value;
Object[] structValues = struct.getAttributes();
String firstName = null, lastName = null;
for (int j=0; j < registerSubTypes.size(); j++) {
/*
* The variable 'registerSubTypes'
* contains the names of the names of the
* subfields.
*/
String subFieldName =
registerSubTypes.get(j).getName();
switch (subFieldName) {
case "first_name":
firstName = (String) structValues[j];
break;
case "last_name":
lastName = (String) structValues[j];
break;
}
/*
* ...
*/
}
} else if (metaData.getColumnType(i)==Types.ARRAY) {
/*
* The JDBC API represents the values of type
* 'array' as 'Array' objects.
*/
Object[] register =
(Object[]) rs.getArray(i).getArray();
for (Object o : register) {
/*
* In the Denodo JDBC API, the content of an
* 'Array' is an array of 'Struct' objects.
*/
Struct s = (Struct) o;
/*
* ...
*/
}
} // else ...
}
}
}
/*
* Close ResultSet, Statement and Connection.
*/
}
受信シリアライズデータをフィルタするアプリケーションからの接続¶
Denodo JDBC ドライバーは、Java Remote Method Invocation (RMI) を使用して Virtual DataPort に接続します。一部のクライアントアプリケーション (Oracle WebLogic Server の最新バージョンなど) には、RMI を使用して受信したデータをフィルタするメカニズムが組み込まれています。このメカニズムを使用すると、クライアントアプリケーションのセキュリティが強化されます。しかし、アプリケーションにこのフィルタがデフォルトで構成されていると、Denodo からのデータを受信できなくなる場合があります。
このフィルタが有効になっているアプリケーションから Denodo に接続する場合、アプリケーションの管理者は、このフィルタを制御するパラメータに次の文字列を追加する必要があります。
com.denodo.**
このフィルタの設定は、クライアントアプリケーションによって異なります。
この詳細については、公式の仕様である「 JEP 290: Filter Incoming Serialization Data 」を参照してください。
日時リテラルを使用したクエリ¶
JDBC ステートメントでエスケープ処理が有効化されている場合、Denodo JDBC ドライバーはタイムスタンプ、日付、および時刻リテラルの JDBC エスケープ構文を、タイムスタンプの正しい VQL 構文と同等と解釈します。つまり、タイムスタンプリテラルの構文は次のとおりであり、
{ts 'yyyy-mm-dd hh:mm:ss.f...'}
日付リテラルの構文は次のとおりであり、
{d 'yyyy-mm-dd'}
時刻リテラルの構文は次のとおりです。
{t 'hh:mm:ss'}
たとえば、次のクエリ
SELECT *
FROM my_view
WHERE time_field = {t '20:00:03'}
AND date_field = {d '1999-01-09'}
AND timestamp_field = {ts '1999-01-09 20:11:11.123455'}
は、JDBC ドライバーによって処理され、次のように Virtual DataPort に送信されます。
SELECT *
FROM my_view
WHERE time_field = TIME '20:00:03'
AND date_field = DATE '1999-01-09'
AND timestamp_field = TIMESTAMP '1999-01-09 20:11:11.123455'
IDU ステートメントによる自動生成キーの取得¶
JDBC ドライバーは、基盤となるデータベースが行を挿入する際に生成する値の取得をサポートしています。多くの場合、自動生成される値は新しい行のプライマリキーです。
例:
Statement stmt = conn.createStatement();
/* With "Statement.RETURN_GENERATED_KEYS", this code requests Virtual DataPort
to return the auto-generated values. */
int rows = stmt.executeUpdate("INSERT INTO customer " +
"(first_name, last_name) " +
"VALUES ('John', 'Smith')",
Statement.RETURN_GENERATED_KEYS);
ResultSet rs = stmt.getGeneratedKeys();
boolean b = rs.next();
if (b == true) {
// Retrieve the new key value
Long customerId = rs.getLong(1);
...
}
自動生成キーを取得するには、以下のいずれかのメソッドを使用して、IDU ステートメント (「INSERT」「UPDATE」「DELETE」) を実行する必要があります。
boolean execute(String sql, int autoGeneratedKeys)
(2 番目の引数は「Statement.RETURN_GENERATED_KEYS」とする)boolean execute(String sql, int[] columnIndexes)
boolean execute(String sql, String[] columnNames)
int executeUpdate(String sql, int autoGeneratedKeys)
(2 番目の引数は「Statement.RETURN_GENERATED_KEYS」とする)int executeUpdate(String sql, int[] columnIndexes)
int executeUpdate(String sql, String[] columnNames)
long executeLargeBatch(String sql, int autoGeneratedKeys)
(2 番目の引数は「Statement.RETURN_GENERATED_KEYS」とする)long executeLargeBatch(String sql, int[] columnIndexes)
long executeLargeBatch(String sql, String[] columnNames)
java.sql.Statement
クラスまたは java.sql.PreparedStatement
クラスを使用できます。
Virtual DataPort 自体はデータを保存しないため、この機能は基盤となるデータベースのサポートに依存します。すべてのデータベースベンダーがこの機能を実装しているわけではありません。たとえば、アプリケーションが JDBC ドライバーを使用して Virtual DataPort に接続し、このアプリケーションが上記のようなコードを実行するとします。「customer」ビューのデータが Oracle に保存されている場合、自動生成された値が Oracle によって Virtual DataPort に返され、ドライバーによってアプリケーションに渡されます。別のデータベースを使用している場合は、この動作をテストして、そのデータベースが値を返すことを確認してください。
内部的な仕組み
アプリケーションから、生成されたキーを返すようリクエストされた場合、ドライバーは Virtual DataPort に送信する IDU クエリを次のように変更します。
アプリケーションからすべての生成されたキーの取得をリクエストされた場合 (
Statement.RETURN_GENERATED_KEYS
)、ドライバーは IDU ステートメントに次を追加します。RETURNING ALL GENERATED KEYS
アプリケーションから、特定のフィールドの自動生成された値を名前を指定してリクエストされた場合、ドライバーは IDU ステートメントに次のようなものを追加します。
RETURNING field_name_2, field_name_4, field_name_5
アプリケーションから、特定のフィールドの自動生成された値をインデックスを指定してリクエストされた場合、ドライバーは IDU ステートメントに次のようなものを追加します。
RETURNING (3, 7, 11)