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; public class Fruit { private String id; 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; } 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; public interface FruitRepository extends BasicRepository<Fruit, String> { CursoredPage<Fruit> cursor(PageRequest pageRequest, Sort<Fruit> order); 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; public 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( 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( 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; 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; } public Iterable<Fruit> offset( long page, int size) { var pageRequest = PageRequest.ofPage(page).size(size); return fruitRepository.offSet(pageRequest).content(); } public Iterable<Fruit> cursor( String after, String before, 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=1
2 番目のページを取得するには、次のコマンドを実行します。
curl --location http://localhost:8080/fruits/offset?page=2
5 ページを取得するには、次のコマンドを実行します。
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 のドキュメントを参照してください。