Spring Data RESTのエクステンション
RESTデータアクセスのエンドポイント生成には、REST Data with Panacheを使用することが推奨されますが、Quarkusでは、 spring-data-rest
エクステンションとしてSpring Data RESTの互換性レイヤーを提供しています。
前提条件
このガイドを完成させるには、以下が必要です:
-
約15分
-
IDE
-
JDK 17+がインストールされ、
JAVA_HOME
が適切に設定されていること -
Apache Maven 3.9.8
-
使用したい場合は、 Quarkus CLI
-
ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること
ソリューション
次の章で紹介する手順に沿って、ステップを踏んでアプリを作成することをお勧めします。ただし、完成した例にそのまま進んでも構いません。
Gitレポジトリをクローンするか git clone https://github.com/quarkusio/quarkus-quickstarts.git
、 アーカイブ をダウンロードします。
ソリューションは spring-data-rest-quickstart
ディレクトリ にあります。
Mavenプロジェクトの作成
まず、新しいプロジェクトが必要です。以下のコマンドで新規プロジェクトを作成します。
Windowsユーザーの場合:
-
cmdを使用する場合、(バックスラッシュ
\
を使用せず、すべてを同じ行に書かないでください)。 -
Powershellを使用する場合は、
-D
パラメータを二重引用符で囲んでください。例:"-DprojectArtifactId=spring-data-rest-quickstart"
このコマンドは、 spring-data-rest
のエクステンションを持つ Maven プロジェクトを生成します。
すでにQuarkusプロジェクトが設定されている場合は、プロジェクトのベースディレクトリで以下のコマンドを実行することで、 spring-data-rest
エクステンションをプロジェクトに追加することができます。
quarkus extension add spring-data-rest
./mvnw quarkus:add-extension -Dextensions='spring-data-rest'
./gradlew addExtension --extensions='spring-data-rest'
これにより、ビルドファイルに以下が追加されます。
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-spring-data-rest</artifactId>
</dependency>
implementation("io.quarkus:quarkus-spring-data-rest")
さらに、以下の依存関係を追加する必要があります。
テストのためには、REST Assuredも必要です。これをビルドファイルに追加します。
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
testImplementation("io.rest-assured:rest-assured")
注: resteasy-jackson
と resteasy-jsonb
の両方がサポートされており、交換することができます。
エンティティの定義
このガイドでは、以下のJPA Entityを使用しています。
package org.acme.spring.data.rest;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
@Entity
public class Fruit {
@Id
@GeneratedValue
private Long id;
private String name;
private String color;
public Fruit() {
}
public Fruit(String name, String color) {
this.name = name;
this.color = color;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
データベースアクセスプロパティの設定
application.properties
に以下のプロパティを追加して、ローカルの PostgreSQL インスタンスへのアクセスを設定します。
quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=quarkus_test
quarkus.datasource.password=quarkus_test
quarkus.datasource.jdbc.url=jdbc:postgresql:quarkus_test
quarkus.datasource.jdbc.max-size=8
quarkus.hibernate-orm.database.generation=drop-and-create
この設定では、PostgreSQLがローカルで動作していることを前提としています。
それを実現する非常に簡単な方法は、以下のDockerコマンドを使用することです。
docker run -it --rm=true --name quarkus_test -e POSTGRES_USER=quarkus_test -e POSTGRES_PASSWORD=quarkus_test -e POSTGRES_DB=quarkus_test -p 5432:5432 postgres:14.1
別のセットアップを使用する予定の場合は、それに合わせて application.properties
を変更してください。
データの準備
QuarkusにおけるSpring Data RESTの機能の一部を簡単に紹介するために、以下の内容を src/main/resources/import.sql
という名前の新しいファイルに追加して、いくつかのテストデータをデータベースに挿入する必要があります:
INSERT INTO fruit(id, name, color) VALUES (1, 'Cherry', 'Red');
INSERT INTO fruit(id, name, color) VALUES (2, 'Apple', 'Red');
INSERT INTO fruit(id, name, color) VALUES (3, 'Banana', 'Yellow');
INSERT INTO fruit(id, name, color) VALUES (4, 'Avocado', 'Green');
INSERT INTO fruit(id, name, color) VALUES (5, 'Strawberry', 'Red');
Hibernate ORMは、アプリケーションの起動時にこれらのクエリを実行します。
リポジトリの定義
次に、 Fruit
にアクセスするためのリポジトリを定義します。典型的なSpring Dataのやり方で、以下のようにリポジトリを作成します:
package org.acme.spring.data.rest;
import org.springframework.data.repository.CrudRepository;
public interface FruitsRepository extends CrudRepository<Fruit, Long> {
}
上記の FruitsRepository
は、Spring Dataの org.springframework.data.repository.CrudRepository
を拡張しており、後者のメソッドはすべて FruitsRepository
で利用できることを意味しています。
spring-data-jpa
エクステンションは、このリポジトリに対する実装を生成します。そして、 spring-data-rest
エクステンションは、そのための REST CRUD リソースを生成します。
テストの更新
FruitsRepository
の機能をテストするために、 FruitsRepositoryTest
のコンテンツを次のように更新します。
package org.acme.spring.data.rest;
import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.core.IsNot.not;
@QuarkusTest
class FruitsRepositoryTest {
@Test
void testListAllFruits() {
//List all, should have all 3 fruits the database has initially:
given()
.accept("application/json")
.when().get("/fruits")
.then()
.statusCode(200)
.body(
containsString("Cherry"),
containsString("Apple"),
containsString("Banana")
);
//Delete the Cherry:
given()
.when().delete("/fruits/1")
.then()
.statusCode(204);
//List all, cherry should be missing now:
given()
.accept("application/json")
.when().get("/fruits")
.then()
.statusCode(200)
.body(
not(containsString("Cherry")),
containsString("Apple"),
containsString("Banana")
);
//Create a new Fruit
given()
.contentType("application/json")
.accept("application/json")
.body("{\"name\": \"Orange\", \"color\": \"Orange\"}")
.when().post("/fruits")
.then()
.statusCode(201)
.body(containsString("Orange"))
.body("id", notNullValue())
.extract().body().jsonPath().getString("id");
//List all, Orange should be present now:
given()
.accept("application/json")
.when().get("/fruits")
.then()
.statusCode(200)
.body(
not(containsString("Cherry")),
containsString("Apple"),
containsString("Orange")
);
}
}
このテストは、 ./mvnw test
を発行することで簡単に実行できます。
./mvnw test
./gradlew test
アプリケーションをパッケージ化して実行する
Quarkus 開発モードは、他の Quarkus エクステンションと同じように、定義済みリポジトリーで機能し、開発サイクル中の生産性を大幅に向上させます。アプリケーションは、通常どおり、以下を使用して開発モードで起動できます。
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
アプリケーションをネイティブ・バイナリとして実行
もちろん、 ネイティブ実行可能ファイルの作成 ガイドの手順に従ってネイティブ実行可能ファイルを作成することもできます。
サポートされているSpring Data RESTの機能
Quarkusは現在、Spring Data RESTの機能のうち、最も便利でよく使われる機能のサブセットをサポートしています。
サポートされている内容
以下のセクションでは、Spring Data RESTの最も重要なサポートされている機能について説明します。
RESTエンドポイントの自動生成
以下のSpring Dataリポジトリを拡張するインターフェイスは、自動的にRESTエンドポイントが生成されます:
-
org.springframework.data.repository.CrudRepository
-
org.springframework.data.repository.PagingAndSortingRepository
-
org.springframework.data.jpa.repository.JpaRepository
上記のリポジトリから生成されたエンドポイントは、5つの一般的なREST操作を提供します。
-
GET /fruits
- すべてのエンティティのリストを返却するか、PagingAndSortingRepository
またはJpaRepository
が使用されている場合はページを返します。 -
GET /fruits/:id
- IDに基づいてエンティティを返却します。 -
POST /fruits
- 新しいエンティティを作成します。 -
PUT /fruits/:id
- 既存のエンティティを更新するか、指定されたIDで新しいエンティティを作成します(エンティティ定義で許可されている場合)。 -
DELETE /fruits/:id
- ID に基づいてエンティティを削除します。
サポートされているデータタイプは、 application/json
と application/hal+json
の2種類です。デフォルトでは前者が使用されますが、 Accept
のヘッダーでどちらを使用するか指定することを強くお勧めします。
大量のエンティティの公開
データベースに大量のエンティティが含まれている場合、それらをすべて一度に返すのは得策ではないかもしれません。 PagingAndSortingRepository
では、 spring-data-rest
エクステンションを使って、データをチャンク単位でアクセスすることができます。
So, you can extend the PagingAndSortingRepository
:
package org.acme.spring.data.rest;
import org.springframework.data.repository.PagingAndSortingRepository;
public interface FruitsRepository extends CrudRepository<Fruit, Long>, PagingAndSortingRepository<Fruit, Long> {
}
これで GET /fruits
は、 sort
、 page
、 size
という3つの新しいクエリパラメータを受け付けるようになりました。
クエリーパラメーター | 説明 | デフォルト値 | 値の例 |
---|---|---|---|
|
list操作で返されたエンティティをソートします |
"" |
|
|
0始まりのインデックスのページ番号。無効な値は0と解釈されます。 |
0 |
0, 11, 100 |
|
ページサイズ。最小値は1で、それ以下の値は1と解釈されます。 |
20 |
1, 11, 100 |
ページングされたレスポンスの場合、 spring-data-rest
は、他のページにアクセスするために使用できるリンクヘッダーのセット(first、previous、next、last)も返します。
Additionally, rather than extending both PagingAndSortingRepository
and CrudRepository
, you can use JpaRepository
, which is a higher-level abstraction tailored for JPA. Since JpaRepository
already extends both PagingAndSortingRepository
and CrudRepository
, it can replace CrudRepository
directly.
package org.acme.spring.data.rest;
import org.springframework.data.repository.PagingAndSortingRepository;
public interface FruitsRepository extends JpaRepository<Fruit, Long> {
}
エンドポイント生成の微調整
これにより、どのメソッドを公開し、どのパスを使ってアクセスするかを指定できます。Spring Data RESTには @RepositoryRestResource
と @RestResource
という2つのアノテーションがあります。 spring-data-rest
エクステンションはこれらのアノテーションの exported
, path
collectionResourceRel
属性をサポートしています。
例えば、フルーツリポジトリは /my-fruits
のパスでアクセスでき、 GET
の操作しかできないようにする必要があるとします。このような場合、 FruitsRepository
は次のようになります:
package org.acme.spring.data.rest;
import java.util.Optional;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import org.springframework.data.rest.core.annotation.RestResource;
@RepositoryRestResource(exported = false, path = "/my-fruits")
public interface FruitsRepository extends CrudRepository<Fruit, Long> {
@RestResource(exported = true)
Optional<Fruit> findById(Long id);
@RestResource(exported = true)
Iterable<Fruit> findAll();
}
spring-data-rest
は、データアクセスにリポジトリメソッドのサブセットのみを使用します。RESTエンドポイントをカスタマイズするためには、正しいメソッドにアノテーションを付けることが重要です。
REST operation | CrudRepository | PagingAndSortingRepository と JpaRepository |
---|---|---|
IDによるGet |
|
|
List |
|
|
Create |
|
|
Update |
|
|
Delete |
|
|
エンドポイントのセキュア化
このエクステンションは、リソースインターフェイスに定義されているパッケージ jakarta.annotation.security
内の Security アノテーションを自動的に使用します。
import jakarta.annotation.security.DenyAll;
import jakarta.annotation.security.RolesAllowed;
@DenyAll
public interface FruitResource extends CrudRepository<Fruit, Long> {
@RolesAllowed("superuser")
Iterable<Fruit> findAll();
}
なお、この機能は、このエクステンションがフード下で使用しているREST Data with Panacheエクステンションによって提供されています。そのため、純粋なSpring Bootアプリケーションでは、同じように動作しないかもしれません。
重要な技術的注意点
Quarkus での Spring サポートは、Spring アプリケーションコンテキストを開始せず、Spring インフラストラクチャークラスも実行されないことに注意してください。Spring クラスとアノテーションは、メタデータの読み取りにのみ使用されるか、ユーザーコードメソッドのリターンタイプまたはパラメータータイプとして使用されます。