2 つめの Quarkus アプリケーション
このチュートリアルでは、データベースへの書き込みとデータベースからの読み込みを行うアプリケーションの作成方法を説明します。 Dev Services を使用するので、実際にデータベースをダウンロードしたり、設定したり、起動したりすることはありません。 また、Hibernate ORM の上のレイヤーである Panache を使用して、データの読み書きを簡単にします。
このガイドは以下についてサポートします:
-
データベースへのオブジェクトの読み書き
-
ゼロ・コンフィギュレーションでサービスを開発し、テストする
要件
このガイドを完成させるには、以下が必要です:
-
ざっと 30 minutes
-
IDE
-
JDK 17+がインストールされ、
JAVA_HOME
が適切に設定されていること -
Apache Maven 3.9.9
-
動作するコンテナランタイム(Docker, Podman)
-
使用したい場合は、 Quarkus CLI
このチュートリアルは、 最初のQuarkusアプリケーションの作成 で学んだことを基に構成されています。 そのアプリケーションのコードは必要ありませんが、コンセプトは理解しておいてください。
ソリューション
[Bootstrapping the project] 以降の指示に従って、アプリケーションを段階的に作成することを推奨します。
ただし、アプリケーションの完成例にそのまま進んでも構いません。
アーカイブ をダウンロードするか、git リポジトリーをクローンします。
git clone https://github.com/quarkusio/quarkus-quickstarts.git
ソリューションは getting-started-dev-services
ディレクトリー にあります。
1. 概略手順
-
アプリケーションのブートストラップ
-
ユーザー入力を読み取るためにアプリケーションを更新
-
Panache エンティティの作成
-
エンティティの読み書き
-
プロファイルを使用して外部データベースを設定
2. インタラクティブ・アプリケーションの設定
2.1. プロジェクトのブートストラップ
新しい Quarkus プロジェクトを作成する最も簡単な方法は、ターミナルを開いて以下のコマンドを実行することです。
Windowsユーザーの場合:
-
cmdを使用する場合、(バックスラッシュ
\
を使用せず、すべてを同じ行に書かないでください)。 -
Powershellを使用する場合は、
-D
パラメータを二重引用符で囲んでください。例:"-DprojectArtifactId=getting-started-dev-services"
生成されたアプリケーションの中身の説明については、 最初のアプリケーションガイド を参照してください。
2.2. アプリケーションの実行
アプリケーションを開発モードで起動します。
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
アプリケーションが起動したら、 http://localhost:8080/hello にアクセスします。"Hello from Quarkus REST" というメッセージが表示されます。
2.3. ユーザー入力の受け入れ
アプリケーションをもう少しインタラクティブにしてみましょう。
IDE でプロジェクトを開き、 src/main/java/org/acme/GreetingResource.java
に移動します。
hello
メソッドにクエリーパラメーターを追加します。
(org.jboss.resteasy.reactive.RestQuery`アノテーションは Jakarta REST の `@QueryParam
アノテーションに似ています。
ただし、パラメーター名を複製する必要はありません。)
public String hello(@RestQuery String name) {
return "Hello " + name;
}
http://localhost:8080/hello?name=Bloom にアクセスします。
パーソナライズされたメッセージ Hello Bloom
が表示されます。
2.4. テストの修正
Quarkus ターミナルで 'r' と入力してテストを実行します。 アプリケーションの変更によってテストが失敗したことを確認できます。
テストを修正するには src/test/java/org/acme/GreetingResourceTest.java
を開き、
以下を
.body(is("Hello from Quarkus REST"));
以下に置き換えます。
.body(containsString("Hello"));
これは引き続き HTTP エンドポイントを検証しますが、 期待される出力に対してより柔軟になります。 ターミナルでテストが成功したことを確認できます。
3. 永続性の追加
3.1. Panache エンティティーの作成
-
永続化ライブラリーを追加するには、以下を実行してください。
quarkus extension add hibernate-orm-panache,jdbc-postgresql
./mvnw quarkus:add-extension -Dextensions='hibernate-orm-panache,jdbc-postgresql'
./gradlew addExtension --extensions='hibernate-orm-panache,jdbc-postgresql'
アプリケーションは、挨拶した人の名前を記録します。 Greeting.java
クラスを作成して、
エンティティーを定義します。以下の内容を追加します。
import io.quarkus.hibernate.orm.panache.PanacheEntity;
import jakarta.persistence.Entity;
@Entity
public class Greeting extends PanacheEntity {
public String name;
}
このエンティティーは、Hibernate ORM の上のレイヤーである Panache を使用します。
PanacheEntity
を拡張すると、データの読み取り、書き込み、検索のためのさまざまなメソッドが導入されます。
すべてのデータアクセスメソッドは、別のデータアクセスクラスではなく、 `Greeting`エンティティー上にあるため、
これはアクティブレコードパターンの例です。
Greeting
テーブルには name
というフィールドの列が 1 つあります。
3.2. データの書き込み
新しいエンティティーを使用するには、 hello
メソッドを更新してデータの書き込みを開始します。
メソッドを以下のように変更します。
@GET
@Transactional
@Produces(MediaType.TEXT_PLAIN)
public String hello(@QueryParam("name") String name) {
Greeting greeting = new Greeting();
greeting.name = name;
greeting.persist();
return "Hello " + name;
}
@Transactional
アノテーションを忘れずに追加してください。これにより、書き込み処理がトランザクション内で
実行されるようになります。
GET はアプリケーションの状態を変更できません。
一般的に、 GET REST メソッドで状態の更新を行うことはできませんが、
ここでは、試しやすくするために更新しています。ここで書き込まれるものは、意味のある状態の変更ではなく、
ログの「副作用」と考えることにしましょう。
|
http://localhost:8080/hello?name=Bloom にアクセスして、更新されたエンドポイントを試してください。 "Hello Bloom" というメッセージが表示されます。
3.3. データの読み取り
新しい永続化コードはエラーなく動作しているように見えますが、 実際にデータベースに書き込まれていることは、どのように確認しますか?
GreetingResource
に 2 番目の REST メソッドを追加します。
@GET
@Path("names")
@Produces(MediaType.TEXT_PLAIN)
public String names() {
List<Greeting> greetings = Greeting.listAll();
String names = greetings.stream().map(g-> g.name)
.collect(Collectors.joining (", "));
return "I've said hello to " + names;
}
試す場合は、 http://localhost:8080/hello?name=Bloom にアクセスしてから、 http://localhost:8080/hello/names にアクセスします。
"I’ve said hello to Bloom" というメッセージが表示されます。
コンテナーランタイムが必要です。
コンテナーランタイムが利用可能である必要がある点を忘れないでください。 そうしないと、この時点で Quarkus のログにエラーが表示され始めます。 |
4. Dev services
データベースの読み取りと書き込みは正常に動作しているようですが、これは少し予想外のことです。 PostgreSQL データベースは、何も設定されていないはずですが、どこから取得されたのですか?
データベースは Dev Services を使用して管理されています。
Dev Services は、アプリケーションに必要なサービスの停止と開始を処理します。
jdbc-postgresql
依存関係を含めたため、データベースはコンテナー化された PostgreSQL データベースです。
代わりに jdbc-mysql
を追加した場合は、コンテナー化された MySQL データベースが取得されたはずです。
必要に応じて、コンテナーツールを使用し、実行中のコンテナーを確認します。
たとえば、Docker を使用している場合は docker ps
を実行し、podman の場合は podman ps
を実行します。
以下のように表示されるはずです。
ff88dcedd899 docker.io/library/postgres:14 postgres -c fsync... 20 minutes ago Up 20 minutes 0.0.0.0:34789->5432/tcp nostalgic_bassi
Quarkus を停止し、再度 docker ps
を実行します。
何も実行されていないはずです (コンテナーがシャットダウンするまでに数分かかる場合があります)。
アプリケーションが停止すると、Quarkus はコンテナーを自動的に停止します。
4.1. サービスの初期化
コードをもう少しいじってみると、アプリケーションを変更した後、 http://localhost:8080/hello/names に名前が表示されない場合があることに気付くかもしれません。
何が起こっているのでしょうか? デフォルトでは、開発モードでは、Dev Services データベースを使用して、
Quarkus は、Hibernate ORM データベース生成を drop-and-create
に設定します。
詳細は、Hibernate 設定リファレンス を参照してください。
コードの変更によりアプリケーションの再起動がトリガーされると、データベーステーブルは
ドロップ (削除) されてから再作成されます。
これは便利ですが、データベースに常にコンテンツが保存されている方がよい場合はどうすればよいですか?
そうすればテストが簡単になります。
import.sql
ファイルを提供すると、Quarkus はそれを使用して、
毎回の起動時にデータベースを初期化します。
-
プロジェクトに
src/main/resources/import.sql
ファイルを作成します。 -
以下の SQL ステートメントを追加します。
INSERT INTO Greeting(id, name)
VALUES (nextval('Greeting_SEQ'), 'Alice');
INSERT INTO Greeting(id, name)
VALUES (nextval('Greeting_SEQ'), 'Bob');
次に、開発モードセッションで s
を押して、強制的に完全に再起動します。続いて、 http://localhost:8080/hello/names にアクセスします。
Alice と Bob が常に名前のリストに含まれていることがわかります。
5. Dev Services の制御
5.1. 外部データベースを代用する
自分で管理する外部データベースを使用する場合はどうすればよいですか?
以下を src/main/resources/application.properties
追加します。
# configure your datasource
quarkus.datasource.db-kind = postgresql
quarkus.datasource.username = leopold
quarkus.datasource.password = bloom
quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:5432/mydatabase
これは、Quarkus に対して Dev Service を起動しないよう指示するものです。 なぜなら、独自のデータベースを使用するためです。ここでは設定の変更方法を確認しているだけなので、 データベースを起動する必要はありません。
http://localhost:8080/hello/names にアクセスします。名前のリストの代わりに、 Quarkus が実行されているターミナルに赤いエラー画面が表示されます。 以下のスタックエラーメッセージが表示されます。
2023-06-28 19:18:22,880 ERROR [io.qua.ver.htt.run.QuarkusErrorHandler] (executor-thread-1) HTTP Request to /hello?name=fred failed, error id: 4f9b5ce6-3b08-41c5-af36-24eee4d1dd2b-2: org.hibernate.exception.JDBCConnectionException: Unable to acquire JDBC Connection [Connection to localhost:5432 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.] [n/a] at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:98) at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:56) ...
これは理にかなっています。データベースの Dev Service を無効にしましたが、 独自のデータベースを起動していません。
5.2. プロファイルの使用
特に必要でなければ、接続エラーを解決するために 外部データベースを設定する必要はありません。代わりに、Dev Service を再び使用します。 その方が簡単です。
しかし、実稼働環境ではどうでしょうか? 実稼働環境では Dev Services を使用したくないはずです。 実際、Quarkus は開発モードとテストモードでのみ Dev Services を起動します。
外部データベースを設定しつつ、 実稼働環境で のみ 使用し、それ以外の時は引き続き Dev Services を使えるようにできたら便利だと思いませんか?
%prod.
を追加します。
データベース設定の接頭辞。これは、この設定が
本番プロファイル にのみ適用されることを意味します。
設定は以下のようになります。
# configure your datasource
%prod.quarkus.datasource.db-kind = postgresql
%prod.quarkus.datasource.username = leopold
%prod.quarkus.datasource.password = bloom
%prod.quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:5432/mydatabase
これで外部データベースが本番モードで使用されるようになり、 Dev Services が開発モードとテストモードで使用されるようになります。
http://localhost:8080/hello/names を確認します。Dev Services が再度有効化されたので、 再び動作しているはずです。 これらの変更のいずれにおいても、Quarkus を再起動する必要がなかったことに注意してください。