PanacheでJakarta RESTリソースを生成する
多くのWebアプリケーションは、REST APIを使った単調なCRUDアプリケーションで、書くのが面倒です。このタスクを合理化するために、REST Data with Panache エクステンションは、エンティティーやリポジトリの基本的なCRUDエンドポイントを生成することができます。
Currently, this extension supports Hibernate ORM and MongoDB with Panache and can generate CRUD resources that work with application/json
and application/hal+json
content.
REST Data with Panacheのセットアップ
Quarkusは、REST Data with Panacheをセットアップするために、以下のエクステンションを提供しています。次の互換性表を参照して、使用している技術に応じて適切なものを使用してください:
エクステンション | ステータス | Hibernate ORM | RESTEasy |
---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
Hibernate ORM
-
必要な依存関係を
pom.xml
に追加します-
Hibernate ORM REST Data with Panache エクステンション (
quarkus-hibernate-orm-rest-data-panache
) -
JDBC ドライバーエクステンション (
quarkus-jdbc-postgresql
,quarkus-jdbc-h2
,quarkus-jdbc-mariadb
, …) -
RESTEasy JSONシリアライゼーションエクステンションの1つ(このエクステンションは、Quarkus REST(旧RESTEasy Reactive)とRESTEasy Classicの両方をサポートしています。)
-
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm-rest-data-panache</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
<!-- Use this if you are using Quarkus REST -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-jackson</artifactId>
</dependency>
<!-- Use this if you are going to use RESTEasy Classic -->
<!--
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jackson</artifactId>
</dependency>
-->
implementation("io.quarkus:quarkus-hibernate-orm-rest-data-panache")
implementation("io.quarkus:quarkus-jdbc-postgresql")
// Use this if you are using Quarkus REST
implementation("io.quarkus:quarkus-rest-jackson")
// Use this if you are going to use RESTEasy Classic
// implementation("io.quarkus:quarkus-resteasy-jackson")
-
Hibernate ORM with Panache ガイドで説明されているように、Panache エンティティおよび/またはリポジトリを実装します。
-
リソースの生成 のセクションで説明したように、生成のためのインターフェイスを定義します。
Panacheを使用したHibernate ORM REST Dataの動作を確認するには、 hibernate-orm-rest-data-panache-quickstart クイックスタートをご覧ください。
Hibernate Reactive
-
必要な依存関係を
pom.xml
に追加します-
Hibernate ORM REST Data with Panache エクステンション (
quarkus-hibernate-orm-rest-data-panache
) -
Vert.x リアクティブデータベースドライバエクステンション (
quarkus-reactive-pg-client
,quarkus-reactive-mysql-client
, …) -
One of the Quarkus REST serialization extensions (
quarkus-rest-jackson
,quarkus-rest-jsonb
, …)
-
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-reactive-rest-data-panache</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-reactive-pg-client</artifactId>
</dependency>
<!-- Use this if you are using REST Jackson for serialization -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-jackson</artifactId>
</dependency>
</dependencies>
-
Hibernate Reactive with Panache ガイドの説明に従って、Panacheエンティティやリポジトリを実装します。
-
リソース生成 のセクションで説明したように、生成のためのインターフェイスを定義します。
MongoDB
-
必要な依存関係を
pom.xml
に追加します-
MongoDB REST Data with Panache エクステンション (
quarkus-mongodb-rest-data-panache
) -
RESTEasy JSONシリアライゼーションエクステンションの1つ (
quarkus-rest-jackson
またはquarkus-rest-jsonb
)
-
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-mongodb-rest-data-panache</artifactId>
</dependency>
<!-- Use this if you are using Quarkus REST -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-jackson</artifactId>
</dependency>
<!-- Use this if you are going to use RESTEasy Classic -->
<!--
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jackson</artifactId>
</dependency>
-->
implementation("io.quarkus:quarkus-mongodb-rest-data-panache")
// Use this if you are using Quarkus REST
implementation("io.quarkus:quarkus-rest-jackson")
// Use this if you are going to use RESTEasy Classic
// implementation("io.quarkus:quarkus-resteasy-jackson")
-
MongoDB with Panache ガイドで説明されているように、Panacheエンティティやリポジトリを実装します。
-
リソース生成 のセクションで説明したように、生成のためのインターフェイスを定義します。
リソースの生成
REST Data with Panacheは、アプリケーションに存在するインターフェースに基づいて、Jakarta RESTリソースを生成します。生成したい各エンティティとリポジトリに対して、リソース・インターフェースを提供します。 これらのインターフェイスは実装せず、カスタムメソッドを提供しないようにしてください。 ただし、カスタマイズするために、拡張インターフェースからメソッドをオーバーライドすることは可能です(末尾のセクションを参照)。
PanacheEntityResource
アプリケーションに PanacheEntity
または PanacheEntityBase
クラスを拡張したエンティティ (例: Person
) がある場合、REST Data with Panache に指示して、次のインターフェイスで Jakarta REST リソースを生成させることができます:
public interface PeopleResource extends PanacheEntityResource<Person, Long> {
}
PanacheRepositoryResource
アプリケーションにシンプルなエンティティ(例: Person
)と、 PanacheRepository
または PanacheRepositoryBase
インターフェースを実装するリポジトリ(例: PersonRepository
)がある場合、REST Data with Panache に指示して、次のインターフェイスで Jakarta REST リソースを生成させることができます:
public interface PeopleResource extends PanacheRepositoryResource<PersonRepository, Person, Long> {
}
PanacheMongoEntityResource
アプリケーションに PanacheMongoEntity
または PanacheMongoEntityBase
クラスを拡張したエンティティ (例: Person
) がある場合、REST Data with Panache に指示して、次のインターフェイスで Jakarta REST リソースを生成させることができます:
public interface PeopleResource extends PanacheMongoEntityResource<Person, Long> {
}
PanacheMongoRepositoryResource
アプリケーションにシンプルなエンティティ(例: Person
)と、 PanacheMongoRepository
または PanacheMongoRepositoryBase
インターフェースを実装するリポジトリ(例: PersonRepository
)がある場合、REST Data with Panache に指示して、次のインターフェイスで Jakarta REST リソースを生成させることができます:
public interface PeopleResource extends PanacheMongoRepositoryResource<PersonRepository, Person, Long> {
}
生成されたリソース
生成されるリソースは、エンティティーとリポジトリの両方で機能的に同等となります。唯一の違いは、利用時の特定のデータアクセスパターンとデータストレージです。
上記の PeopleResource
インターフェイスのいずれかを定義している場合、このエクステンションは特定のデータアクセス戦略を使用してその実装を生成します。
実装されたクラスは生成された Jakarta REST リソースで使われます:
public class PeopleResourceJaxRs { // The actual class name is going to be unique
@Inject
PeopleResource resource;
@GET
@Path("{id}")
@Produces("application/json")
public Person get(@PathParam("id") Long id){
Person person = resource.get(id);
if (person == null) {
throw new WebApplicationException(404);
}
return person;
}
@GET
@Produces("application/json")
public Response list(@QueryParam("sort") List<String> sortQuery,
@QueryParam("page") @DefaultValue("0") int pageIndex,
@QueryParam("size") @DefaultValue("20") int pageSize) {
Page page = Page.of(pageIndex, pageSize);
Sort sort = getSortFromQuery(sortQuery);
List<Person> people = resource.list(page, sort);
// ... build a response with page links and return a 200 response with a list
}
@GET
@Path("/count")
public long count() {
return resource.count();
}
@Transactional
@POST
@Consumes("application/json")
@Produces("application/json")
public Response add(Person personToSave) {
Person person = resource.add(person);
// ... build a new location URL and return 201 response with an entity
}
@Transactional
@PUT
@Path("{id}")
@Consumes("application/json")
@Produces("application/json")
public Response update(@PathParam("id") Long id, Person personToSave) {
if (resource.get(id) == null) {
Person person = resource.update(id, personToSave);
return Response.status(204).build();
}
Person person = resource.update(id, personToSave);
// ... build a new location URL and return 201 response with an entity
}
@Transactional
@DELETE
@Path("{id}")
public void delete(@PathParam("id") Long id) {
if (!resource.delete(id)) {
throw new WebApplicationException(404);
}
}
}
リソースのカスタマイズ
REST Data with Panacheは、リソースの特定の機能をカスタマイズするために使用できる @ResourceProperties
、 @MethodProperties
アノテーションを提供します。
リソースインターフェイスで使用することができます:
@ResourceProperties(hal = true, path = "my-people")
public interface PeopleResource extends PanacheEntityResource<Person, Long> {
@MethodProperties(path = "all")
List<Person> list(Page page, Sort sort);
@MethodProperties(exposed = false)
boolean delete(Long id);
}
利用可能なオプション
@ResourceProperties
-
exposed
- リソースが公開される可能性があるかどうか。各メソッドに対してオーバーライド可能なグローバルリソースプロパティー。デフォルトはtrue
です。 -
path
- リソースのベースパス。デフォルトのパスは、resource
またはcontroller
のサフィックスを含まないハイフン付きの小文字のリソース名です。 -
rolesAllowed
- リソースへのアクセスが許可されているセキュリティロールのリストです。Quarkus Securityエクステンションが存在する必要があり、存在しない場合は無視されます。デフォルトは空です。 -
paged
- コレクションのレスポンスをページングするかどうか。最初、最後、前、次のページの URI が存在する場合は、レスポンスヘッダに含まれます。リクエストページのインデックスとサイズは、page
とsize
のクエリパラメーターから取得され、それぞれのデフォルトは0
と20
です。デフォルトはtrue
です。 -
hal
- 標準のapplication/json
レスポンスに加えて、Accept
ヘッダでリクエストされた場合にapplication/hal+json
レスポンスを返す追加のメソッドを生成します。デフォルトはfalse
です。 -
halCollectionName
- HAL コレクションレスポンスを生成する際に使用されるべき名前です。デフォルトの名前はresource
またはcontroller
のサフィックスなしのハイフン付き小文字のリソース名です。
@MethodProperties
-
exposed
-false
に設定されている場合、特定の HTTP Verb を公開しません。デフォルトはtrue
です。 -
path
- 操作パス (これはリソースベースのパスに追加されます)。デフォルトは空の文字列です。 -
rolesAllowed
- この操作へのアクセスが許可されているセキュリティロールのリストです。Quarkus Securityエクステンションが存在する必要があり、存在しない場合は無視されます。デフォルトは空です。
生成されたリソースへの追加メソッドの追加
REST Data with Panache エクステンションによって生成されたリソースに対して、リソースインターフェイスにこれらのメソッドを追加することで、追加メソッドを追加することができます。例:
@ResourceProperties
public interface PeopleResource extends PanacheEntityResource<Person, Long> {
@GET
@Path("/name/{name}")
@Produces("application/json")
default List<Person> findByName(@PathParam("name") String name) {
return Person.find("name = :name", Collections.singletonMap("name", name)).list();
}
}
そして、このメソッドは、生成されたメソッドとともに、 http://localhost:8080/people/name/Johan
で公開されます。
エンドポイントのセキュア化
REST Data with Panacheは、リソースインターフェースに定義されたパッケージ jakarta.annotation.security
内のSecurityアノテーションを使用します:
import jakarta.annotation.security.DenyAll;
import jakarta.annotation.security.RolesAllowed;
@DenyAll
@ResourceProperties
public interface PeopleResource extends PanacheEntityResource<Person, Long> {
@RolesAllowed("superuser")
boolean delete(Long id);
}
さらに、リソースの使用が許可されているロールの指定にのみ関心がある場合、 @ResourceProperties
および @MethodProperties
のアノテーションには、リソースまたは操作へのアクセスが許可されているセキュリティロールをリストアップするフィールド rolesAllowed
があります。
エンティティをリストアップするクエリパラメータ
REST Data with Panacheは、エンティティのリストを取得するために以下のクエリパラメータをサポートしています:
-
page
- リスト操作で返されるべきページ番号。これはページ化されたリソースにのみ適用され、0 から始まる番号です。 デフォルトは 0 です。 -
size
- リスト操作で返されるべきページサイズ。これはページ化されたリソースにのみ適用され、1から始まる数値です。 デフォルトは20です。 -
sort
- リスト操作の結果をソートするために使われるべきフィールドのカンマ区切りのリスト。フィールドの前に-
を付けない限り、フィールドは昇順でソートされます。例えば?sort=name,-age
は、名前の昇順、年齢の降順で結果をソートします。 -
namedQuery
- `@NamedQuery`アノテーションを用いてエンティティレベルで設定されるべき名前付きクエリです。
たとえば、最初のページで2つの People
エンティティを取得したい場合、 http://localhost:8080/people?page=0&size=2
を呼び出します。応答は次のようになる筈です:
[
{
"id": 1,
"name": "John Johnson",
"birth": "1988-01-10"
},
{
"id": 2,
"name": "Peter Peterson",
"birth": "1986-11-20"
}
]
さらに、フィールド名と値を含むクエリパラメータを追加することで、エンティティフィールドでフィルタリングすることも可能です。 http://localhost:8080/people?name=Peter Peterson
を呼び出すと、次が返却されるでしょう。
[
{
"id": 2,
"name": "Peter Peterson",
"birth": "1986-11-20"
}
]
フィールドによるフィルタリングは、String、Boolean、Character、Double、Float、Integer、Long、Short、Byteおよびプリミティブ型にのみ対応しています。 |
@NamedQueryを使った複雑なフィルタリングによるエンティティのリスト化
エンティティのリストアップ時にフィルタリングする名前付きクエリを指定することができます。例えば、エンティティに次のような名前付きクエリを持つことです:
@Entity
@NamedQuery(name = "Person.containsInName", query = "from Person where name like CONCAT('%', CONCAT(:name, '%'))")
public class Person extends PanacheEntity {
String name;
}
この例では、 name
フィールドにテキストを含むすべての人物をリストアップする名前付きクエリを追加しています。
次に、使用したい名前付きクエリの名前で生成されたリソースを使用してエンティティを一覧表示するときに、クエリ パラメータ namedQuery
を設定できます。 http://localhost:8080/people?namedQuery=Person.containsInName&name=ter
の呼出は、名前に "ter"という文字列が含まれる人物をすべて返します。
名前付きクエリの動作の詳細については、 Hibernate ORM ガイドまたは Hibernate Reactive ガイドにアクセスしてください。
リソースメソッド Before/Afterリスナー
REST Data with Panacheは、以下のリソースメソッドフックの購読をサポートします:
-
Before/After add resource
-
Before/After update resource
-
Before/After delete resource
リソースメソッドリスナーを登録するには、インターフェース RestDataResourceMethodListener
を実装したBeanを提供する必要があります。例:
@ApplicationScoped
public class PeopleRestDataResourceMethodListener implements RestDataResourceMethodListener<Person> {
@Override
public void onBeforeAdd(Person person) {
System.out.println("Before Save Person: " + person.name);
}
}
レスポンスボディの例
前述の通り、REST Data with Panache は application/json
と application/hal+json
のレスポンス コンテンツ タイプをサポートしています。 Person
ここでは、データベース内に get
レコードが 5 つあると仮定して、 と list
操作を行った場合のレスポンスボディがどのようになるか、いくつかの例を示します。
GET /people/1
Accept: application/json
{
"id": 1,
"name": "John Johnson",
"birth": "1988-01-10"
}
Accept: application/hal+json
{
"id": 1,
"name": "John Johnson",
"birth": "1988-01-10",
"_links": {
"self": {
"href": "http://example.com/people/1"
},
"remove": {
"href": "http://example.com/people/1"
},
"update": {
"href": "http://example.com/people/1"
},
"add": {
"href": "http://example.com/people"
},
"list": {
"href": "http://example.com/people"
}
}
}
GET /people?page=0&size=2
Accept: application/json
[
{
"id": 1,
"name": "John Johnson",
"birth": "1988-01-10"
},
{
"id": 2,
"name": "Peter Peterson",
"birth": "1986-11-20"
}
]
Accept: application/hal+json
{
"_embedded": [
{
"id": 1,
"name": "John Johnson",
"birth": "1988-01-10",
"_links": {
"self": {
"href": "http://example.com/people/1"
},
"remove": {
"href": "http://example.com/people/1"
},
"update": {
"href": "http://example.com/people/1"
},
"add": {
"href": "http://example.com/people"
},
"list": {
"href": "http://example.com/people"
}
}
},
{
"id": 2,
"name": "Peter Peterson",
"birth": "1986-11-20",
"_links": {
"self": {
"href": "http://example.com/people/2"
},
"remove": {
"href": "http://example.com/people/2"
},
"update": {
"href": "http://example.com/people/2"
},
"add": {
"href": "http://example.com/people"
},
"list": {
"href": "http://example.com/people"
}
}
}
],
"_links": {
"add": {
"href": "http://example.com/people"
},
"list": {
"href": "http://example.com/people"
},
"first": {
"href": "http://example.com/people?page=0&size=2"
},
"last": {
"href": "http://example.com/people?page=2&size=2"
},
"next": {
"href": "http://example.com/people?page=1&size=2"
}
}
}
両方のレスポンスには、これらのヘッダも含まれています:
-
Link: < http://example.com/people?page=0&size=2 >; rel="first"
-
Link: < http://example.com/people?page=2&size=2 >; rel="last"
-
Link: < http://example.com/people?page=1&size=2 >; rel="next"
前のページが存在しないため、 previous
リンクヘッダ(および HAL リンク)が含まれません。
Jakarta RESTクラスのインクルード/エクスクルード
ビルドタイム条件の使用
Quarkusでは、CDI Beanの場合と同様に、ビルド時の条件によって、Jakarta RESTリソース、プロバイダ、フィーチャーを直接追加または除外できます。
したがって、REST Data with Panacheインターフェイスにプロファイル条件( @io.quarkus.arc.profile.IfBuildProfile
または @io.quarkus.arc.profile.UnlessBuildProfile
)やプロパティ条件( io.quarkus.arc.properties.IfBuildProperty
または io.quarkus.arc.properties.UnlessBuildProperty
)をアノテーションすることで、生成されるJakarta RESTクラスをどの条件で含めるかを、ビルド時にQuarkusに指示することができます。
以下の例では、ビルドプロファイル app1
が有効になっている場合に限り、Quarkus は PeopleResource
インターフェースから生成されたリソースを含めます。
@IfBuildProfile("app1")
public interface PeopleResource extends PanacheEntityResource<Person, Long> {
}
実行時プロパティの使用
このオプションは、Quarkus REST Quarkusエクステンションを使用している場合にのみ使用できます。 |
Quarkusでは、 @io.quarkus.resteasy.reactive.server.EndpointDisabled
アノテーションを使用して、実行時プロパティの値に基づいて、生成されたJakarta RESTリソースを条件付きで無効にすることもできます。
以下の例では、アプリケーションが some.property
を "disable"
に設定している場合、Quarkus は生成されたリソースを実行時に PeopleResource
インターフェースから除外します。
@EndpointDisabled(name = "some.property", stringValue = "disable")
public interface PeopleResource extends PanacheEntityResource<Person, Long> {
}