Panacheを利用したJAX-RSリソースの生成
多くのWebアプリケーションは、REST APIを使った単調なCRUDアプリケーションで、書くのが面倒です。このタスクを合理化するために、REST Data with Panache エクステンションは、エンティティーやリポジトリの基本的なCRUDエンドポイントを生成することができます。
このエクステンションはまだ実験的なものであり、限られた機能セットしか提供していませんが、早期にフィードバックが得られることを期待しています。現在のところ、このエクステンションは Hibernate ORM と MongoDB with Panache をサポートしており、 application/json
と application/hal+json
のコンテンツで動作する CRUD リソースを生成することができます。
この技術は、experimentalと考えられています。 experimental モードでは、アイデアを成熟させるために早期のフィードバックが求められます。ソリューションが成熟するまでの間、プラットフォームの安定性や長期的な存在を保証するものではありません。フィードバックは メーリングリスト や GitHubの課題管理 で受け付けています。 For a full list of possible statuses, check our FAQ entry. |
PanacheでRESTデータをセットアップする
Quarkus provides the following extensions to set up REST Data with Panache. Please, check out the next compatibility table to use the right one according to the technology you’re using:
Extension | Hibernate | RESTEasy |
---|---|---|
|
|
|
|
|
|
|
|
Hibernate ORM
-
Add the required dependencies to your build file
-
Hibernate ORM REST Data with Panache エクステンション (
quarkus-hibernate-orm-rest-data-panache
) -
JDBC ドライバーエクステンション (
quarkus-jdbc-postgresql
,quarkus-jdbc-h2
,quarkus-jdbc-mariadb
, …) -
RESTEasy JSON シリアライゼーションエクステンションのどれか ( RESTEasy Classic と RESTEasy Reactive の両方をサポートしたエクステンション)
-
<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 RESTEasy Reactive -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-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 RESTEasy Reactive
implementation("io.quarkus:quarkus-resteasy-reactive-jackson")
// Use this if you are going to use RESTEasy Classic
// implementation("io.quarkus:quarkus-resteasy-jackson")
-
Hibernate ORM with Panacheガイドで説明されているように、Panacheエンティティやリポジトリを実装します。
-
Define the interfaces for generation as explained in the resource generation section.
Hibernate Reactive
-
Add the required dependencies to your
pom.xml
-
Hibernate Reactive REST Data with Panache extension (
quarkus-hibernate-reactive-rest-data-panache
) -
A Vert.x reactive database driver extension (
quarkus-reactive-pg-client
,quarkus-reactive-mysql-client
, …) -
One of the RESTEasy Reactive serialization extensions (
quarkus-resteasy-reactive-jsonb
,quarkus-resteasy-reactive-jackson
, …)
-
<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 RESTEasy Reactive Jackson for serialization -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-jackson</artifactId>
</dependency>
</dependencies>
-
Implement the Panache entities and/or repositories as explained in the Hibernate Reactive with Panache guide.
-
Define the interfaces for generation as explained in the resource generation section.
MongoDB
-
Add the required dependencies to your build file
-
MongoDB REST Data with Panache エクステンション (
quarkus-mongodb-rest-data-panache
) -
RESTEasy JSON シリアライゼーションエクステンションのどれか (
quarkus-resteasy-jackson
またはquarkus-resteasy-jsonb
)
-
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-mongodb-rest-data-panache</artifactId>
</dependency>
<!-- Use this if you are using RESTEasy Reactive -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-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 RESTEasy Reactive
implementation("io.quarkus:quarkus-resteasy-reactive-jackson")
// Use this if you are going to use RESTEasy Classic
// implementation("io.quarkus:quarkus-resteasy-jackson")
-
MongoDB with Panacheガイドで説明されているように、Panacheエンティティやリポジトリを実装します。
-
Define the interfaces for generation as explained in the resource generation section.
リソースの生成
REST Data with Panache は、アプリケーションで利用可能なインターフェイスに基づいて JAX-RS リソースを生成します。生成したい各エンティティーとリポジトリに対して、リソースインターフェイスを提供してください。 これらのインターフェイスを実装したり、カスタムメソッドを提供したりしないでください。 ただし、エクステンションインターフェースからメソッドをオーバーライドしてカスタマイズすることは可能です(最後のセクションを参照してください)。
PanacheEntityResource
アプリケーションに PanacheEntity
または PanacheEntityBase
クラスを拡張したエンティティー ( Person
など) がある場合、REST Data with Panache に、次のインターフェイスを使用して JAX-RS リソースを生成するように指示することができます。
public interface PeopleResource extends PanacheEntityResource<Person, Long> {
}
PanacheRepositoryResource
アプリケーションが単純なエンティティー(例: Person
)と、 PanacheRepository
または PanacheRepositoryBase
インターフェイスを実装したリポジトリ(例: PersonRepository
)を持っている場合、REST Data with Panache に、以下のインターフェイスを使用して JAX-RS リソースを生成するように指示することができます。
public interface PeopleResource extends PanacheRepositoryResource<PersonRepository, Person, Long> {
}
PanacheMongoEntityResource
アプリケーションに PanacheMongoEntity
または PanacheMongoEntityBase
クラスを拡張したエンティティー ( Person
など) がある場合、REST Data with Panache に、次のインターフェイスを使用して JAX-RS リソースを生成するように指示することができます。
public interface PeopleResource extends PanacheMongoEntityResource<Person, Long> {
}
PanacheMongoRepositoryResource
アプリケーションが単純なエンティティー(例: Person
)と、 PanacheMongoRepository
または PanacheMongoRepositoryBase
インターフェイスを実装したリポジトリ(例: PersonRepository
)を持っている場合、REST Data with Panache に、以下のインターフェイスを使用して JAX-RS リソースを生成するように指示することができます。
public interface PeopleResource extends PanacheMongoRepositoryResource<PersonRepository, Person, Long> {
}
生成されたリソース
生成されるリソースは、エンティティーとリポジトリの両方で機能的に同等となります。唯一の違いは、利用時の特定のデータアクセスパターンとデータストレージです。
上記の PeopleResource
インターフェイスのいずれかを定義している場合、このエクステンションは特定のデータアクセス戦略を使用してその実装を生成します。実装されたクラスは、生成された JAX-RS リソースによって使用され、以下のようになります。
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
}
@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
のサフィックスを含まないハイフン付きの小文字のリソース名です。 -
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
- 操作パス (これはリソースベースのパスに追加されます)。デフォルトは空の文字列です。
クエリパラメーター
REST Data with Panacheは、生成されたリソースで以下のクエリパラメーターをサポートしています。
-
page
- リスト操作で返されるべきページ番号。これはページ化されたリソースにのみ適用され、0 から始まる番号です。 デフォルトは 0 です。 -
size
- リスト操作で返されるべきページサイズ。これはページ化されたリソースにのみ適用され、1から始まる数値です。 デフォルトは20です。 -
sort
- リスト操作の結果をソートするために使われるべきフィールドのカンマ区切りのリスト。フィールドの前に-
を付けない限り、フィールドは昇順でソートされます。例えば?sort=name,-age
は、名前の昇順、年齢の降順で結果をソートします。
レスポンスボディの例
前述の通り、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 リンク)が含まれません。