Overview
このチュートリアルでは、MongoDBに接続されたQuarkusアプリケーションにページ区切り手法を実装する方法を説明します。Javascript データリポジトリを使用して、オフセットベースとカーソルベースのページ区切りメソッドの両方をサポートするREST APIエンドポイントを作成する方法を学びます。
ページ分割
ページネーションは、大規模なデータセットを小さく、より管理しやすいチャンクに分割するために使用される手法です。このチュートリアルでは、オフセットベースとカーソルベースのページ区切りメソッドを実装します。オフセットベースのページ分割では、ページ番号を使用してデータの特定のサブセットを検索します。一方、カーソルベースのページ分割では、参照点または カーソル を使用してデータセット内を移動します。
Tutorial
このチュートリアルでは、次のアクションを実行する方法について説明します。
前提条件を確認します
必要な依存関係を持つ Queryusプロジェクトを作成する
MongoDB接続の構成
データエンティティとリポジトリを定義する
ページネーション用のREST APIエンドポイントを実装する
ページネーション エンドポイントとなる接続されたデバイスをテストする
前提条件を確認します。
開始する前に、次の前提条件タスクを完了してください。
MongoDB AtlasまたはローカルDockerインスタンス上のMongoDBクラスターを構成する
Dockerを使用してローカルMongoDBインスタンスを起動するには、次のコマンドを実行します。
docker run --rm -d --name mongodb-instance -p 27017:27017 mongoあるいは、 MongoDB Atlas を使用して、無料の M0 クラスターを配置することもできます。Atlas のアカウントとクラスターの作成方法については、 MongoDB の使用開始 ガイドを参照してください。
Flexibleプロジェクトを作成します。
MongoDB クエリ ジェネレーターに移動し、次の設定でプロジェクトを構成します。
ご希望のグループとアーティファクトIDを選択します。
次の依存関係を追加します。
JNoSQL ドキュメントMongoDB (
quarkus-jnosql-document-mongodb)REST簡単リアクティブ(
quarkus-resteasy-reactive)RESTKey Reactive JSON(
quarkus-resteasy-reactive-jackson)OpenAPI (
quarkus-smallrye-openapi)
プロジェクトを生成し、ZIPファイルをダウンロードして抽出します。
注意
ジェネレーターで依存関係が見つからない場合は、pom.xmlファイルに手動で追加します。
設定が完了したら、pom.xmlファイルに次の依存関係が含まれていることを確認します。
<dependencies> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-smallrye-openapi</artifactId> </dependency> <dependency> <groupId>io.quarkiverse.jnosql</groupId> <artifactId>quarkus-jnosql-document-mongodb</artifactId> <version>3.3.0</version> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-resteasy-reactive</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-resteasy-reactive-jackson</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-arc</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-junit5</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>io.rest-assured</groupId> <artifactId>rest-assured</artifactId> <scope>test</scope> </dependency></dependencies>MongoDBデータベースを構成します。
application.propertiesファイルを開き、 MongoDBインスタンスに接続するために次の構成プロパティを追加します。
quarkus.mongodb.connection-string = <your connection string>jnosql.document.database = fruitsこの構成により、アプリケーションは指定された接続文字列のMongoDBクラスターに接続し、fruitsデータベースを使用できるようになります。
重要
本番環境では、アクセス制御を有効にし、認証を強制します 。詳細については、セキュリティ チェックリスト を参照してください。
環境変数を使用することでこれらのプロパティを上書きできます。これにより、コードを変更せずに開発、テスト、本番用のさまざまな構成を指定できます。
データエンティティを作成します。
src/main/javaディレクトリに Fruit エンティティクラスを作成します。次のコードは、id フィールドと name フィールドを持つエンティティを定義します。
import jakarta.nosql.Column;import jakarta.nosql.Convert;import jakarta.nosql.Entity;import jakarta.nosql.Id;import org.eclipse.jnosql.databases.mongodb.mapping.ObjectIdConverter;
@Entitypublic class Fruit {
@Id @Convert(ObjectIdConverter.class) private String id;
@Column private String name;
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
@Override public String toString() { return "Fruit{" + "id='" + id + '\'' + ", name='" + name + '\'' + '}'; }
public static Fruit of(String name) { Fruit fruit = new Fruit(); fruit.setName(name); return fruit; }
}リポジトリインターフェースを作成します。
BasicRepositoryクラスを拡張する FruitRepository インターフェースを作成します。次のコードでは、オフセットとカーソルベースのページ分割の両方のメソッドを定義します。
import jakarta.data.Sort;import jakarta.data.page.CursoredPage;import jakarta.data.page.Page;import jakarta.data.page.PageRequest;import jakarta.data.repository.BasicRepository;import jakarta.data.repository.Find;import jakarta.data.repository.OrderBy;import jakarta.data.repository.Repository;
@Repositorypublic interface FruitRepository extends BasicRepository<Fruit, String> {
@Find CursoredPage<Fruit> cursor(PageRequest pageRequest, Sort<Fruit> order);
@Find @OrderBy("name") Page<Fruit> offSet(PageRequest pageRequest);
long countBy();
}フレームワークはこのインターフェースを自動的に実装するため、ブロッキングコードを記述せずにデータベース操作を実行できるようになります。
データベース設定クラスを作成します。
src/main/javaディレクトリに SetupDatabaseクラスを作成します。次のコードは、スタートアップ時にデータベースにサンプルデータを入力し、シャットダウン時にそのデータを削除します。
import jakarta.enterprise.context.ApplicationScoped;import jakarta.enterprise.event.Observes;import io.quarkus.runtime.ShutdownEvent;import io.quarkus.runtime.StartupEvent;import org.jboss.logging.Logger;import java.util.List;
@ApplicationScopedpublic class SetupDatabase {
private static final Logger LOGGER = Logger.getLogger(SetupDatabase.class.getName());
private final FruitRepository fruitRepository;
public SetupDatabase(FruitRepository fruitRepository) { this.fruitRepository = fruitRepository; }
void onStart(@Observes StartupEvent ev) { LOGGER.info("The application is starting..."); long count = fruitRepository.countBy(); if (count > 0) { LOGGER.info("Database already populated"); return; } List<Fruit> fruits = List.of( Fruit.of("apple"), Fruit.of("banana"), Fruit.of("cherry"), Fruit.of("date"), Fruit.of("elderberry"), Fruit.of("fig"), Fruit.of("grape"), Fruit.of("honeydew"), Fruit.of("kiwi"), Fruit.of("lemon") ); fruitRepository.saveAll(fruits); }
void onStop(@Observes ShutdownEvent ev) { LOGGER.info("The application is stopping..."); fruitRepository.deleteAll(fruitRepository.findAll().toList()); }
}REST APIエンドポイントを作成します。
src/main/javaディレクトリに FruitResourceクラスを作成します。次に、次のコードをクラスファイルに貼り付けます。
import jakarta.data.Sort;import jakarta.data.page.PageRequest;import jakarta.ws.rs.DefaultValue;import jakarta.ws.rs.GET;import jakarta.ws.rs.Path;import jakarta.ws.rs.Produces;import jakarta.ws.rs.QueryParam;import jakarta.ws.rs.core.MediaType;
@Path("/fruits")public class FruitResource {
private final FruitRepository fruitRepository;
private static final Sort<Fruit> ASC = Sort.asc("name"); private static final Sort<Fruit> DESC = Sort.desc("name");
public FruitResource(FruitRepository fruitRepository) { this.fruitRepository = fruitRepository; }
@Path("/offset") @GET @Produces(MediaType.APPLICATION_JSON) public Iterable<Fruit> offset(@QueryParam("page") @DefaultValue("1") long page, @QueryParam("size") @DefaultValue("2") int size) { var pageRequest = PageRequest.ofPage(page).size(size); return fruitRepository.offSet(pageRequest).content(); }
@Path("/cursor") @GET @Produces(MediaType.APPLICATION_JSON) public Iterable<Fruit> cursor(@QueryParam("after") @DefaultValue("") String after, @QueryParam("before") @DefaultValue("") String before, @QueryParam("size") @DefaultValue("2") int size) { if (!after.isBlank()) { var pageRequest = PageRequest.ofSize(size).afterCursor(PageRequest.Cursor.forKey(after)); return fruitRepository.cursor(pageRequest, ASC).content(); } else if (!before.isBlank()) { var pageRequest = PageRequest.ofSize(size).beforeCursor(PageRequest.Cursor.forKey(before)); return fruitRepository.cursor(pageRequest, DESC).stream().toList(); } var pageRequest = PageRequest.ofSize(size); return fruitRepository.cursor(pageRequest, ASC).content(); }
}このクラスは、次のエンドポイントとなる接続されたデバイスを定義します。
/fruits/offset:pageクエリ パラメータを使用して、オフセットベースのページ分割をサポートします。/fruits/cursor:afterとbeforeクエリ パラメータを使用して、カーソルベースのページ分割をサポートします。
どちらのエンドポイントも、ページあたりのアイテム数を指定するために size クエリ パラメータを受け入れます。
オフセットベースのページ区切りのテスト。
別のターミナルウィンドウで、次の curl コマンドを使用して、オフセット ページネーション エンドポイントをテストします。これらのコマンドは、実稼働データのさまざまなページをリクエスト。
最初のページを取得するには、次のコマンドを実行します。
curl --location http://localhost:8080/fruits/offset?page=12 番目のページを取得するには、次のコマンドを実行します。
curl --location http://localhost:8080/fruits/offset?page=25 ページを取得するには、次のコマンドを実行します。
curl --location http://localhost:8080/fruits/offset?page=5カーソルベースのページ分割のテスト。
カーソル ページネーション エンドポイントをテストするには、次の curl コマンドを使用します。これらのコマンドは、after と before パラメーターを使用してデータセット内を移動します。
結果の初期セットを取得するには、次のコマンドを実行する:
curl --location http://localhost:8080/fruits/cursor"banana" の後に続く nameフィールド値を持つ結果を取得するには、次のコマンドを実行します。
curl --location http://localhost:8080/fruits/cursor?after=banana"date" より前に到着する nameフィールド値を持つ実を取得するには、次のコマンドを実行します。
curl --location http://localhost:8080/fruits/cursor?before=date追加リソース
MongoDBでのページ分割の詳細については、 MongoDB Atlasドキュメントの「 結果のページ分割」ガイドを参照してください。
Quarkus の詳細については、Quarkus のドキュメントを参照してください。