Helidon DB Client

原文はこちら。
The original entry was written by Tomáš Kraus (Senior Software Developer, Oracle).
https://medium.com/helidon/helidon-db-client-e12bbdc85b7

リアクティブアプリケーションでデータベースを操作するのは常に問題がつきまとっていましたが、もう大丈夫です。Helidon 2.0.0-M1でプレリリース版のHelidon DB Clientを導入しました。これはノンブロッキングのデータベース操作のための統合されたリアクティブAPIです。

Helidon 2.0.0-M1
https://github.com/oracle/helidon/releases/tag/2.0.0-M1

Note: Pre-release versions are experimental and not intended for production use. APIs and features are not yet fully tested and are subject to change.
(プレリリースバージョンは実験的なもので、本番環境での利用を意図していません。API、機能はまだ完全にテストされておらず、変更の可能性があります。)

Features Overview

Helidon 2.0 DB Clientには以下の主要機能が含まれています。

  • Unified API for data access and query
    このAPIはJDBCやMongoDB Reactive Streams Java Driver上のレイヤーとして実装されました。そのため、JDBCドライバを持つ任意のリレーショナルデータベースやMongoDBをサポートします。
  • Reactive database access with non-reactive drivers
    ほとんどのJDBCドライバはノンブロッキングではないため、リアクティブアプリケーションで利用すると問題があります。Helidon DB Clientを使うと、executorサービスにブロッキングドライバをラップすることにより、ブロッキングJDBCドライバをリアクティブアプリケーションで利用できます。
  • Observability
    ヘルスチェック、メトリック、トレースのサポートを全てのHelidon SE APIに追加しています。Helidon DB Clientも例外ではありません。
  • Backpressure
    Helidon DB Clientはコンシューマが要求した場合に限りデータベース操作を実行します。これはTCPレイヤーまで伝播されます。
  • Improved portability between relational database drivers
    DB Clientは、コード内のインラインで利用可能だったり、名前付きステートメントとして定義可能だったりする、ネイティブのデータベース・ステートメントを扱います。ネイティブクエリコードを構成ファイルに移行すれば、コードではなく構成ファイルを変えることで、Helidon DB Clientを使って別のデータベースに切り替えることができます。

DB Clientを利用前に構成する必要があります。Helidon Configを使ってJDBCベースのクライアントを構成する方法を見ていきましょう。

Helidon Configurationノードには以下のセクションが含まれています。

  • source: jdbc もしくは mongodb
  • connection: データベース接続パラメータ
  • statements: 利用時の名前付きステートメント

JDBC-based Database Clientの構成は以下の通りです。

db:
  source: "jdbc"
  connection:
    url: "jdbc:mysql://127.0.0.1:3306/pokemon?useSSL=false"
    username: "user"
    password: "password"
    poolName: "mysql"
  statements:
    ping: "DO 0"
    select-all-pokemons: "SELECT id, name FROM Pokemons"

DB Client API

Helidon DB Client APIには、様々なパラメータ付きステートメントを実行するためのメソッドや、ステートメント実行結果を取得するためのメソッドが数多く含まれています。以下の章ではステートメントの構築ならびに実行で利用可能なオプションを説明しています。

Executor Selection

DBClient クラスには、どのステートメントをトランザクションで実行するか否かを選択するための2個のメソッドがあります。

execute(Function<DbExecute, T> executor)
inTransaction(Function<DbTransaction, CompletionStage<T>> executor)

両メソッドには、ステートメントexecutorを持つFunctionインターフェースの引数が必要です。

Statement Building and Execution

DbExecute クラスは様々なステートメントビルダーのためのメソッドを数多く提供しています。

ステートメントの種類メソッド
DMLcreateDmlStatement
createNamedDmlStatement
insertcreateInsert
createNamedInsert
updatecreateUpdate
createNamedUpdate
deletecreateDelete
createNamedDelete
querycreateQuery
createNamedQuery
commoncreateStatement
createNamedStatement

メソッド名に “Named” が付いているものは、ステートメント構成セクションからステートメント名を予期します。

全てのステートメントビルダーはステートメントパラメータを設定するメソッドを提供します。これらのパラメータは順序付きパラメータもしくは名前付きパラメータの利用が可能です。順序付きもしくは名前付きパラメータを1ステートメント中に混在させることはできません

Ordered Parameters

順序付きパラメータをSQLステートメント内で?として書き留めます。

SELECT name FROM Pokemons WHERE id = ?

これらはJDBCのPreparedStatementのパラメータと同等です。

順序付きパラメータを設定するメソッドは以下の通りです。

メソッド補足
params(List<?> parameters)全てのパラメータはList
params(Object… parameters)全てのパラメータは配列
indexedParam(Object parameters)登録済みマッパーとともに使うPOJO
addParam(Object parameter)1個のパラメータ
繰り返しの呼び出し可能

Named parameters

名前付きパラメータは :<name> としてSQLステートメント内に書き留めます。

SELECT name FROM Pokemons WHERE id = :id

MongoDBステートメントでは $<name> の表記を使います。

{
    "collection": "pokemons",
    "operation": "update",
    "value":{ $set: { "name": $name } },
    "query": { id: $id }
}

名前付きパラメータを設定するメソッドは以下の通りです。

メソッド補足
params(Map<String, ?> parameters)全てのパラメータはMap
namedParam(Object parameters)登録済みマッパーとともに使うPOJO
namedParam(Object parameters)1個のパラメータ
繰り返しの呼び出し可能

Statement Execution

ステートメントのパラメータを設定後、execute() メソッドを呼び出してステートメントを実行します。このメソッドの戻り値はCompletionStage<R>です。ここでRはステートメント実行結果です。

順序付きパラメータを持つJDBCクエリをトランザクションに入れずに実行する例です。

dbClient.execute(exec -> exec
    .createQuery("SELECT name FROM Pokemons WHERE id = ?")
    .params(1)
    .execute()
);

名前付きパラメータを持つJDBCクエリをトランザクション内で実行する場合の呼び出し例です。

dbClient.inTransaction(tx -> tx
    .createQuery("SELECT name FROM Pokemons WHERE id = :id")
    .addParam("id", 1)
    .execute()
);

両方の例で、クエリが返す行を伴ってCompletionStage<DbRows<DbRow>>を返します。

この例ではMongoDBで名前付きパラメータを持つupdateステートメントをトランザクションに入れずに実行する例です。

dbClient.execute(exec -> exec
    .createUpdate("{\"collection\": \"pokemons\","
        + "\"value\":{$set:{\"name\":$name}},"
        + "\"query\":{id:$id}}")
    .addParam("id", 1)
    .addParam("name", "Pikachu")
    .execute()
);

このupdateステートメントはデータベース内で変更された行数を伴ってCompletionStage<Long>を返します。

DML Statement Result

DMLステートメントの実行では、常にでデータベース内で変更された行数を伴ってCompletionStage<Long>を返します。

以下の例では、変更された行数を標準出力に表示しています。

dbClient.execute(exec -> exec
    .insert("INSERT INTO Pokemons (id, name) VALUES(?, ?)",
        1, "Pikachu"))
    .thenAccept(count -> 
        System.out.printf("Inserted %d records, count\n"));

Query Statement Result

クエリステートメントの実行は常にCompletionStage<DbRows<DbRow>>を返します。DbRowsクラスはこの結果にアクセスするためのメソッドを提供しています。

メソッド補足
Flow.Publisher<DbRow> publisher() 個々の結果行をFlow.Subscriber<DbRow>を使って処理する
CompletionStage<List<DbRow>> collect()全ての行をList<DbRow>として返す
<U> DbRows<U> map(…)指定されたマッパーを使って戻された結果をマップする

全行の収集ならびにList<DbRow>として処理する方法は以下のpokemonサンプルをご覧ください。

Helidon DB Client Pokemon Example with JDBC
https://github.com/oracle/helidon/tree/master/examples/dbclient/pokemons

Samples

このサンプルプロジェクトがDB Clientの手始めとしてお役に立つことでしょう。リレーショナルデータベースとMongoDBの両方でHelidon DB Clientを使う方法を説明するサンプルは以下からご利用頂けます。

Helidon DB Client Examples
https://github.com/oracle/helidon/tree/master/examples/dbclient/

Summary

Helidon DB Clientはまだ実験的な機能であり、最終化までに皆様からのフィードバックが必要です。是非お試しいただいて、原文のコメントに改善点を残したり、GitHubにIssueを立てたり、Slackでお知らせください。

GitHub Issues
https://github.com/oracle/helidon/issues
Slack参加のURL

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Google フォト

Google アカウントを使ってコメントしています。 ログアウト /  変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中