小编典典

使用Hibernate条件和DISTINCT_ROOT_ENTITY分页

hibernate

我已经使用以下代码实现了分页:

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个结果。但是我应该得到8个结果(使用DISTINCT),因为当我手动构建SQL时没有区别时,我得到11个结果,而当我使用DISTINCT时,我正确地得到了8个结果。

上面的代码有什么问题?


阅读 537

收藏
2020-06-20

共1个答案

小编典典

在为我的问题寻找解决方案很长时间之后,我设法解决了它。如果您创建一个使用JOINS检索到许多关联的条件或查询,然后使用setMaxResults并将ResultTransformer设置为DISTINCT_ROOT_ENTITY的问题将不会达到您期望的结果。

就像JB Nizet所说的,假设您有4个A实体,每个实体有3个B实体,并且假设您的查询检索了所有A实体及其B。

在这种情况下,SQL查询将返回12行。如果使用setMaxResults(7),它将检索(例如)A1及其B的三行,A2及其B的三行,A3及其第一个B的仅一行。

而且,由于您使用了DISTINCT_ROOT_ENTITY,所以条件查询将仅返回三个实体:A1,A2和A3(它们的B集合不完整)。

要解决此问题,必须将toMany(通常是集合)关系的FETCH MODE设置为SELECT或SUBSELECT,基本上有两种方法可以实现:

第一种方法是在属性上使用@FetchMode(FetchMode.SUBSELECT)批注,我不喜欢这种方法,因为它会导致每个查询都使用SUBSELECT
FETCH来检索集合。但这会起作用。

另一种方法是在建立查询时为关系设置获取模式。我更喜欢这种方式,因为我可以根据需要自定义查询,而不必对所有查询都使用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

2020-06-20