15

次のコードを使用して、ページネーションを既に実装しています。

public Paginacao<Anuncio> consultarPaginado(int pagina, Integer cidadeId) {

            Criteria criteria = this.sessionFactory.getCurrentSession().createCriteria(Anuncio.class);      
            criteria.add(Restrictions.eq("ativo", true));
            criteria.add(Restrictions.eq("statusLiberacao", AnunciosUtil.STATUS_ANUNCIO_LIBERADO));
            criteria.add(Restrictions.eq("statusVendaAnuncio", AnunciosUtil.STATUS_VENDA_ANUNCIO_DISPONIVEL));

            if (cidadeId != null) {
                criteria.add(Restrictions.eq("cidade.id", cidadeId));
            }

            criteria.addOrder(Order.desc("dataPostagem"));
            criteria.setProjection(Projections.rowCount());

            Long count = (Long) criteria.uniqueResult();

            Paginacao<Anuncio> paginacao = new Paginacao<Anuncio>();
            int qtdPaginas = (count.intValue() / 7) + 1;

            paginacao.setQtdPaginas(qtdPaginas);

            criteria.setProjection(null);// reseta a criteria sem a projeção
            criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);

            if (pagina > qtdPaginas) {
                pagina = qtdPaginas;
            }
            pagina = pagina - 1;
            criteria.setFirstResult(pagina * ConstantesGenericas.MAXIMO_OBJETOS_RETORNADOS);
            criteria.setMaxResults(ConstantesGenericas.MAXIMO_OBJETOS_RETORNADOS);

            paginacao.setRegistros(criteria.list());

            return paginacao;
        }

SQL クエリを手動で作成してデータベースに送信すると、8 つの結果が得られます。ただし、上記のコードを試してみると、ResultTransformer を DISTINCT_ROOT_ENTITY に設定する前に 8 つの結果 (個別なし) が得られ、設定後に 4 つの結果が得られます。しかし、(DISTINCT を使用して) 8 つの結果を取得する必要があります。これは、個別の SQL を手動で作成すると 11 の結果が得られ、DISTINCT を使用すると正しく 8 つの個別の結果が得られるためです。

上記のコードの何が問題になっていますか?

4

3 に答える 3

25

私の問題の解決策を長い間探した後、私はそれを解決することができました。JOINS を使用して toMany 関連付けを取得する条件またはクエリを作成し、次に setMaxResults を使用して ResultTransformer を DISTINCT_ROOT_ENTITY に設定すると、結果が期待どおりにならないという問題。

JB Nizet が言ったように、4 つの A エンティティがあり、それぞれに 3 つの B エンティティがあり、クエリがすべての A エンティティとその B を取得するとします。

その場合、SQL クエリは 12 行を返します。setMaxResults(7) を使用すると、(たとえば) A1 とその B の 3 行、A2 とその B の 3 行、A3 とその最初の B の 1 行だけが取得されます。

また、DISTINCT_ROOT_ENTITY を使用したため、基準クエリは A1、A2、および A3 の 3 つのエンティティのみを返します (これには B の不完全なセットがあります)。

これを解決するには、toMany (通常はコレクション) 関係の FETCH MODE を SELECT または SUBSELECT に設定する必要があります。これを実現するには、基本的に 2 つの方法があります。

最初の方法は、属性に @FetchMode(FetchMode.SUBSELECT) アノテーションを使用することです。このアプローチは好きではありません。これは、すべてのクエリで SUBSELECT FETCH を使用してコレクションを取得するためです。しかし、それはうまくいきます。

もう 1 つの方法は、クエリを作成するときにリレーションシップのフェッチ モードを設定することです。必要に応じてクエリをカスタマイズでき、すべてのクエリに SUBSELECTS を使用する必要がないため、私はこの方法を好みます。だから、私はこのようにしました:

public Paginacao<Anuncio> consultarPaginado(int pagina, Integer cidadeId) {

        Criteria criteria = this.sessionFactory.getCurrentSession().createCriteria(Anuncio.class);      
        criteria.add(Restrictions.eq("ativo", true));
        criteria.add(Restrictions.eq("statusLiberacao", AnunciosUtil.STATUS_ANUNCIO_LIBERADO));
        criteria.add(Restrictions.eq("statusVendaAnuncio", AnunciosUtil.STATUS_VENDA_ANUNCIO_DISPONIVEL));
        criteria.setFetchMode("imagens", FetchMode.SELECT);
        criteria.setFetchMode("pagamentos", FetchMode.SELECT);      

        if (cidadeId != null) {
            criteria.add(Restrictions.eq("cidade.id", cidadeId));
        }

        criteria.addOrder(Order.desc("dataPostagem"));
        criteria.setProjection(Projections.rowCount());

        Long count = (Long) criteria.uniqueResult();

        Paginacao<Anuncio> paginacao = new Paginacao<Anuncio>();
        int qtdPaginas = (count.intValue() / 7) + 1;

        paginacao.setQtdPaginas(qtdPaginas);

        criteria.setProjection(null);// reseta a criteria sem a projeção
        criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);

        if (pagina > qtdPaginas) {
            pagina = qtdPaginas;
        }
        pagina = pagina - 1;
        criteria.setFirstResult(pagina * ConstantesGenericas.MAXIMO_OBJETOS_RETORNADOS);
        criteria.setMaxResults(ConstantesGenericas.MAXIMO_OBJETOS_RETORNADOS);

        paginacao.setRegistros(criteria.list());

        return paginacao;
    }

それが他の誰かに役立つことを願っています。;D

于 2012-06-14T18:07:00.057 に答える
13

あなたの質問を正しく理解しているかどうかはわかりませんが、クエリがtoManyアソシエーションが結合フェッチされたエンティティを取得する場合、ページネーションは期待どおりに機能しません。

実際、4つのAエンティティがあり、それぞれに3つのBエンティティがあり、クエリがすべてのAエンティティとそのBを取得するとします。

その場合、SQLクエリは12行を返します。setMaxResults(7)を使用すると、たとえば、A1とそのBに3行、A2とそのBに3行、A3とその最初のBに1行だけが取得されます。

また、DISTINCT_ROOT_ENTITYを使用したため、条件クエリはA1、A2、およびA3(Bの不完全なセットを持つ)の3つのエンティティのみを返します。

于 2012-06-14T17:34:38.873 に答える
12

これは私にとって問題であり、私が持っているすべてのシナリオで機能する解決策を見つけるのに時間がかかりました.

各ページネーション ページに必要なのは、すべての結果の合計数と結果の単一ページの 2 つですが、そのためには 3 つの手順を実行する必要があります。1) 合計数を取得し、2) ページの一意の ID を取得し、3) ステップ 2 で見つかった ID の完全なデータを取得します。これらすべてを 1 つの条件オブジェクトで実行できます。

1)個別のIDを使用して合計数を取得します(uniqueField =エンティティクラスのIDの名前)

  Criteria criteria = session.createCriteria(YourEntity.class);
  Projection idCountProjection = Projections.countDistinct(uniqueField);
  criteria.setProjection(idCountProjection);
  //setup criteria, joins etc here
  int totalResultCount = ((Long)criteria.uniqueResult()).intValue();

2)投影をリセットし、開始と長さを設定します(個別のIDが必要です)

  criteria.setProjection(Projections.distinct(Projections.property(uniqueField)));
  criteria.setFirstResult(start); 
  criteria.setMaxResults(length);
  List uniqueSubList = criteria.list();

3) プロジェクションをリセットし、ID に一致する個別の結果を得る

  criteria.setProjection(null);
  criteria.setFirstResult(0); criteria.setMaxResults(Integer.MAX_VALUE);
  criteria.add(Restrictions.in(uniqueField, uniqueSubList));
  criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
  List searchResults = criteria.list();
  //and now, however you want to return your results
  Map<String, Object> searchResultsMap = new HashMap<String, Object>();
  searchResultsMap.put("searchResults", searchResults);
  searchResultsMap.put("totalResultCount", totalResultCount);
于 2014-05-12T20:24:30.923 に答える