Spring + queryDSLでページングする方法(+ Pageable)

2023/09/24に公開

Spring + queryDSLでページングする方法(+ Pageable)

  • Spring Boot3.1.3
  • Java17
  • queryDSL
  • JPA

SpringでPaging処理をするよく使われる方法は二通りある。

  1. Pageableを使用する方法
  2. queryDSLを使用する方法
    • Pageable + queryDSLも使用できるが一般的に使う組み合わせではない。
    • カスタムrequestオブジェクトとqueryDSLの組み合わせがよく使われる。

Pageable

ControllerでPageableを因数として受け取る

PostController
@GetMapping("/posts")
public List<PostResponse> getList(Pageable pageable) {
    return postService.getList(pageable);
}

Spring Data JPAのJpaRepositoryを具現したPostRepositoryを使用している。

  • JpaRepositoryが提供するfindAllメソッドにPageableを渡すことが出来る
PostService
public List<PostResponse> getList(Pageable pageable) {
    return postRepository
        .findAll(pageable)
        .stream()
        .map(PostResponse::new)
        .toList();
}

ページサイズの指定やソートはquery parameterで受け取る

ページの順番を指定

ページの順番は0から始まる。

"/posts?page=0"

ページの順番を0ではなく1から始まりたい場合にはapplication.propertiesに以下のオプションを使う。

applicaiton.properties
# http リクエストのみ効果がある
spring.data.web.pageable.one-indexed-parameters=true

サイズを指定する

1ページ当たりサイズを指定する

"/posts?page=0&size=5"

以下の設定でサイズの基本値を付与可能

applcation.properties
spring.data.web.pageable.default-page-size=5

ソート

idを基準としてdesc整列。

  • ,に注意
"/posts?page=0&size=5&sort=id,desc"

queryDSLを使う方法

requestオブジェクトを生成

Pagination用のrequestオブジェクトを生成する

PostSearch.java
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class PostSearch {

  private static final int MAX_SIZE = 200;

  @Builder.Default
  private Integer page = 1;
  @Builder.Default
  private Integer size = 10;

  public long getOffset() {
    return (long) (Math.max(1, page) - 1) * Math.min(size, MAX_SIZE);
  }

}

カスタムPostRepositoryCustomインターフェースを生成

queryDSLを使用してPaging処理をするためにカスタムPostRepositoryCustomインターフェースを生成する。

PostRepositoryCustom
public interface PostRepositoryCustom {
  List<Post> getList(PostSearch postSearch);
}

具現クラスを生成

具現クラスを生成する
staticインポートを使えばもっと綺麗に出来る。

PostRepositoryImpl
@RequiredArgsConstructor
public class PostRepositoryImpl implements PostRepositoryCustom {

  private final JPAQueryFactory query;

  @Override
  public List<Post> getList(PostSearch postSearch) {
    return query
        .selectFrom(QPost.post)
        .limit(postSearch.getSize()) // サイズ
        .offset(postSearch.getOffset()) // offset
        .orderBy(QPost.post.id.desc()) // ソート
        .fetch();
  }
}

PostRepositoryに拡張させる

具現したクラスをPostRepositoryに拡張させる

PostRepository
public interface PostRepository extends JpaRepository<Post, Long>, PostRepositoryCustom {
}

PostControllerとPostServiceで使用

postcontrollerで使用する

PostController
@GetMapping("/posts")
public List<PostResponse> getList(@ModelAttribute PostSearch request) {
    return postService.getList(request);
}

postServiceで使用する

PostService
public List<PostResponse> getList(PostSearch postSearch) {
    return postRepository.getList(postSearch)
        .stream()
        .map(PostResponse::new)
        .toList();
}

これでクライアントからリクエスト時にquery parameterで値を指定して送ればいい

"/posts?page=1&size=20"

後追加的なpaging関連query parameterや基本値の変更等が必要な場合はPostSearchに追加、修正すれば良い。

Discussion