web-dev-qa-db-ja.com

Spring Data PageImplが正しいサイズのページを返さない?

データベースから取得したオブジェクトのリストを使用して、新しいPageを作成しようとしています。まず、DBからすべての要素を取得し、それをStreamに変換してから、ラムダを使用して結果をフィルタリングします。次に、設定された数の要素を持つページが必要ですが、新しいPageImplをインスタンス化しても、正しいサイズのページが返されないようです。

これが私のコードです:

_List<Produtos> listaFinal;
Stream<Produtos> stream = produtosRepository.findAll().stream();
listaFinal = stream.filter(p -> p.getProdNome().contains("uio")).collect(Collectors.toList());

long total = listaFinal.size();
Page<Produtos> imp = new PageImpl<>(listaFinal,pageable,total);
_

デバッグのスクリーンショットは次のとおりです。

Pageableオブジェクトのサイズは20に設定されており、70個の要素をレンダリングするには4ページが必要であることを理解していますが、リスト全体を返します。

何が足りないのですか?

トーマスのコメントに答えて編集:

Pageを使用してデータの一部だけを返す方法を理解しています。私が示したコードは、ラムダ式を使用してコレクションをフィルタリングしようとしたものです。私にとっての問題は、Java 8のラムダを使用して、Spring DataJPAを介してデータベースにクエリを実行したいことです。VB.NETおよびエンティティfunction(x)クエリ式に慣れていて、疑問に思っていました。 SpringJPAで同じことを行う方法。

私のリポジトリでは、findAll(Predicate,Pageable)にアクセスできる_extends JpaRepository<Produtos, Integer>, QueryDslPredicateExecutor<Produtos>_を使用しています。ただし、述語は入力されていないため、クエリでp -> p.getProdNome().contains("uio")を使用することはできません。 SQLServerとHibernateを使用しています。

10
dubonzi

Spring Dataの仕組みについて詳しく学んだ後、JpaRepository実装内のメソッドで@Queryアノテーションを使用して、DBに適切にクエリを実行し、結果をフィルタリングして、ストリームを使用してからPageに戻す必要をなくしました。

誰かが例を必要とする場合に、上記のコードがどのように見えるかを次に示します。

@Query("select p from Produtos p where p.prodNome = ?1")
public Page<Produtos> productsListByName(String prodNome, Pageable pageable)

SpringのfindByメソッドを知っていますが、パラメーターの量によってはメソッド名が読みづらくなることがあるので、JPQLに固執しました。

このようにすると、ページのコンテンツには常に、Spring構成で定義された最大数の要素が含まれます。

また、PageImplのカスタム実装を使用しています。現在作業しておらず、コードにアクセスできませんが、できる限り投稿します。

編集:カスタム実装を見つけることができます ここ

4
dubonzi

stites'answer を拡張するには、 PagedListHolder が進むべき道であり、その方法は次のとおりです。

List<String> list = // ...

// Creation
PagedListHolder page = new PagedListHolder(list);
page.setPageSize(10); // number of items per page
page.setPage(0);      // set to first page

// Retrieval
page.getPageCount(); // number of pages 
page.getPageList();  // a List which represents the current page

並べ替えが必要な場合は、別の PagedListHolderコンストラクターMutableSortDefinition を使用します。

6
Willi Mentzel

私があなたのコードを正しく理解していれば、あなたの意図はデータベースからすべてのレコードをロードし、それらをPageImplに収集されるx個のバケットに分割することです。

それはそれが以前はどのように機能していたかではありません。 PageableおよびPage抽象化の実際の目的は、すべてのデータをクエリする必要はなく、必要なデータの「スライス」だけをクエリすることです。

あなたの場合、Page<X> page = repository.findAll(pageable);を介してデータをクエリし、それを返すことができます。ページは、現在のページのレコードを、レコードの総数や次のページがあるかどうかなどの追加情報とともに保持します。

クライアントコードでは、その情報を使用してレコードのリストをレンダリングし、次/前のリンクを適切に生成できます。結果タイプとしてPage<X>を使用するクエリは、2つのクエリを発行することに注意してください(1つはクエリの総数を決定し、1つは実際のページデータを決定します)。

結果の総数に関する情報は必要ないが、次のリンクを生成できるようにしたい場合は、戻り値の型としてSlice<X>を使用する必要があります。これは1つのクエリしか発行しないためです。

3
Thomas Darimont

PageImplは、リストのページ付けを実行することを目的としたものではありません。 ドキュメントから これは単なる「基本的なPage実装」であり、ほとんどあなたが望むもののように聞こえますが、それは本当に誤解を招くものです。

PagedListHolder を使用します。これは、オブジェクトのリストを処理してページに分割するための単純な状態ホルダーです。

3
stites

答えを追加するには遅すぎます。しかし、私も同じ問題に直面していて、その方法を見つけました。 SimpleJpaRepositoryにはメソッドがあります

_public Page<T> findAll(Specification<T> spec, Pageable pageable) {

        TypedQuery<T> query = getQuery(spec, pageable);
        return pageable == null ? new PageImpl<T>(query.getResultList())
                : readPage(query, getDomainClass(), pageable, spec);
    }
_

これは、JpaRepositoryを拡張する場合に_Page<T>_を返すために使用されます。したがって、ここでも同じ機能を使用できます(Springは完全なページ付けをサポートするパブリックメソッドを提供していないため、コードを書き直す必要があります)。

メソッドPageImpl<>(List<T> content, Pageable pageable, long total);を見ると、pageableで指定した値が設定されているだけです。ここでは、完全なリストとしてcontentを送信していますが、Springは内部目的のために送信しません。 Page<Produtos> imp = new PageImpl<>(listaFinal,pageable,total);を次のコードブロックに置き換える必要があります。

_TypedQuery<User> query = entityManager.createQuery(criteriaQuery); // Users type can be replaced with Produtos or any other db entity.

query.setFirstResult(pageable.getOffset());
query.setMaxResults(pageable.getPageSize());

List<User> users= query.getResultList();
Page<User> result=PageableExecutionUtils.getPage(users,pageable, () -> getCountForQuery(User.class));
_

メソッドgetCountForQuery:

_private Long getCountForQuery(Class<?> t){

    CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();

    CriteriaQuery<Long> countQuery = criteriaBuilder
            .createQuery(Long.class);
    countQuery.select(criteriaBuilder.count(
            countQuery.from(t)));
    Long count = entityManager.createQuery(countQuery)
            .getSingleResult();

    return count;
}
_

_PageableExecutionUtils.getPage_の使用法はで見つけることができます

_readPage(TypedQuery<S> query, final Class<S> domainClass, Pageable pageable,
            final Specification<S> spec) 
_

SimpleJpaRepositoryのメソッド。これは主にfindAll内部メソッドによって使用されます。

0
AshwinK

多くの方法を適用した後、これは私の場合の解決策であり、機能しています

>         int pageSize = pageable.getPageSize();
>         long pageOffset = pageable.getOffset();
>         long total = pageOffset + list.size() + (list.size() == pageSize ? pageSize : 0);
>         Page<listType> page = new PageImpl<listType>(list, pageable,total)
0
hatem htira