The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

Panacheを利用したJAX-RSリソースの生成

多くのWebアプリケーションは、REST APIを使った単調なCRUDアプリケーションで、書くのが面倒です。このタスクを合理化するために、REST Data with Panache エクステンションは、エンティティーやリポジトリの基本的なCRUDエンドポイントを生成することができます。

このエクステンションはまだ実験的なものであり、限られた機能セットしか提供していませんが、早期にフィードバックが得られることを期待しています。現在のところ、このエクステンションは Hibernate ORM と MongoDB with Panache をサポートしており、 application/jsonapplication/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:

Table 1. Compatibility Table
Extension Hibernate RESTEasy

quarkus-hibernate-orm-rest-data-panache

ORM

Classic and Reactive

quarkus-hibernate-reactive-rest-data-panache

Reactive

Reactive

quarkus-mongodb-rest-data-panache

ORM

Classic and Reactive

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 の両方をサポートしたエクステンション)

pom.xml
<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>
-->
build.gradle
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 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>

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 )

pom.xml
<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>
-->
build.gradle
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")

リソースの生成

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 が存在する場合は、レスポンスヘッダに含まれます。リクエストページのインデックスとサイズは、 pagesize のクエリパラメーターから取得され、それぞれのデフォルトは 020 です。デフォルトは 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/jsonapplication/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"
    }
  }
}

両方のレスポンスには、これらのヘッダも含まれています。

前のページが存在しないため、 previous リンクヘッダ(および HAL リンク)が含まれません。