原文はこちら。
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 クラスは様々なステートメントビルダーのためのメソッドを数多く提供しています。
ステートメントの種類 | メソッド |
---|---|
DML | createDmlStatement createNamedDmlStatement |
insert | createInsert createNamedInsert |
update | createUpdate createNamedUpdate |
delete | createDelete createNamedDelete |
query | createQuery createNamedQuery |
common | createStatement 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