2012-02-23 22 views
5

Esto parece ser un problema bien conocido durante años como se puede leer aquí: http://blog.xebia.com/2008/12/11/sorting-and-pagination-with-hibernate-criteria-how-it-can-go-wrong-with-joins/Hibernate - resultados distintos con paginación

Y incluso encuentra referencia en preguntas frecuentes de hibernación:

https://community.jboss.org/wiki/HibernateFAQ-AdvancedProblems#Hibernate_does_not_return_distinct_results_for_a_query_with_outer_join_fetching_enabled_for_a_collection_even_if_I_use_the_distinct_keyword

Esto tiene también ha discutido previamente en SO

How to get distinct results in hibernate with joins and row-based limiting (paging)?

El problema es que incluso después de pasar por todos estos recursos, no he podido resolver mi problema, que parece ser un poco diferente de este problema estándar, aunque no estoy seguro.

La solución estándar propuesta aquí implica la creación de dos consultas, la primera para obtener identificadores distintos y luego utilizarlos en una consulta de nivel superior para obtener la paginación deseada. Las clases de hibernación en mi caso son algo así como

A 
- aId 
- Set<B> 

B 
- bId 

Me parece que la subconsulta parece estar funcionando bien para mí y está siendo capaz de obtener las ayudas distintas, pero la consulta externa que se supone que debe hacer la paginación vuelve a buscar los duplicados y, por lo tanto, lo distinto en la subconsulta no tiene ningún efecto.

Suponiendo que tengo una Un objeto que tiene un conjunto de cuatro B objetos, mi análisis es que, debido a la introducción del conjunto, mientras que ir a buscar datos para

session.createCriteria(A.class).list(); 

Hibernate es poblar cuatro referencias en la lista que apunta a solo un objeto Debido a esto, la solución estándar me falla.

¿Podría alguien ayudarme a encontrar una solución para este caso?

Editar: He decidido ir por hacer la paginación por nosotros mismos desde el conjunto de resultados distintos. La otra forma igual de malo podría haber sido a la carga perezosa los objetos B, pero que habría requerido consultas separadas para todos los objetos A a buscar correspondiente B objetos

Respuesta

6

considerar el uso de DistinctRootEntity transformador resultado como este

session.createCriteria(A.class) 
    .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).list(); 

ACTUALIZACIÓN

Las muestras de consultas para asociaciones uno-a-muchos.

public Collection<Long> getIDsOfAs(int pageNumber, int pageSize) { 
    Session session = getCurrentSession(); 

    Criteria criteria = session.createCriteria(A.class) 
     .setProjection(Projections.id()) 
     .addOrder(Order.asc("id")); 

    if(pageNumber >= 0 && pageSize > 0) { 
     criteria.setMaxResults(pageSize); 
     criteria.setFirstResult(pageNumber * pageSize); 
    } 

    @SuppressWarnings("unchecked") 
    Collection<Long> ids = criteria.list(); 
    return ids; 
} 

public Collection<A> getAs(int pageNumber, int pageSize) { 
    Collection<A> as = Collections.emptyList(); 

    Collection<Long> ids = getIDsOfAs(pageNumber, pageSize); 
    if(!ids.isEmpty()) { 
     Session session = getCurrentSession(); 

     Criteria criteria = session.createCriteria(A.class) 
      .add(Restrictions.in("id", ids)) 
      .addOrder(Order.asc("id")) 
      .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY); 
     @SuppressWarnings("unchecked") 
     as = criteria.list(); 
    }  

    return as; 
} 
+0

Lo he usado también, en la subconsulta. Mi subconsulta está funcionando bien y está devolviendo identificaciones únicas. El problema es que la consulta externa está obteniendo duplicados. Por ejemplo, si la subconsulta devuelve A.ids = 1,2,3, la consulta principal (que se utiliza para la paginación), obtiene A.ids duplicados como 1,1,1,2,2,3,3,3 y, por lo tanto, la consulta completa del formulario Seleccione A. * de A en donde A.id en (subconsulta) límite 10; no puede obtener registros únicos A – Ashish

+0

Su consulta final debe tener el siguiente aspecto: seleccione a. * desde a donde a.id in (: ids), y la consulta para obtener identificadores de A debe tener este aspecto: seleccione a.id desde A límite 10. Por lo tanto, aplique Límite solo a los identificadores, no a la consulta final. – szhem

+0

mi consulta final es de la forma seleccione a. * Desde una combinación externa izquierda b en a.id = b.aid y esto es necesario para tener el objeto A completo. – Ashish

1

se menciona la razón que usted está viendo este problema se debe a que Set<B> se trae con impaciencia. Si está paginando, es probable que no necesite el B para cada A, por lo que podría ser mejor buscarlo de forma perezosa.

Sin embargo, este mismo problema se produce cuando se une a B en la consulta para realizar una selección.

En algunos casos, no solo querrá paginar, sino también ordenar en otros campos además del ID.Creo que la manera de hacer esto es la formulación de la consulta como la siguiente:

 
    Criteria filter = session.createCriteria(A.class) 
    .add(... any criteria you want to filter on, including aliases etc ...); 
    filter.setProjection(Projections.id()); 

    Criteria paginate = session.createCriteria(A.class) 
    .add(Subqueries.in("id", filter)) 
    .addOrder(Order.desc("foo")) 
    .setMaxResults(max) 
    .setFirstResult(first); 

    return paginate.list(); 

(pseudocódigo, no comprobó si la sintaxis es exactamente correcto pero se entiende la idea)

+0

Gracias Arnout. Pero crees que esto no tendrá duplicados. Mi problema es que incluso para una pequeña consulta como session.createCriteria (A.class) .list(), me devuelve duplicados debido a la búsqueda ansiosa. ¿Cómo evita su solución la búsqueda ansiosa? – Ashish

+0

Ah, sí: en primer lugar, realmente evaluar si realmente necesita una búsqueda ansiosa. En mi experiencia, casi nunca es una buena idea buscar siempre determinadas colecciones con entusiasmo; puede ser útil buscarlas con entusiasmo en determinadas consultas específicas, por supuesto, y la API de Criteria le permite especificar eso. Si está seguro de que quiere buscar siempre esta colección con entusiasmo, use el 'Criteria.DISTINCT_ROOT_ENTITY'' ResultTransformer' en los Criterios externos ('paginate'). –

0

que respondieron a esta aquí: Pagination with Hibernate Criteria and DISTINCT_ROOT_ENTITY

Tienes que hacer 3 cosas, 1) obtener el recuento total, 2) obtener los identificadores de las filas que deseas, y luego 3) obtener tus datos para los identificadores encontrados en el paso 2. Realmente no es todo tan malo una vez que obtenga el pedido correcto, e incluso puede crear un método genérico y enviar un objeto de criterio separado para hacerlo más abstracto.

0

He usado la propiedad groupBy para lograr esto. Espero que funcione.

Criteria filter = session.createCriteria(A.class);  
filter.setProjection(Projections.groupProperty("aId")); 
//filter.add(Restrictions.eq()); add restrictions if any 
filter.setFirstResult(pageNum*pageSize).setMaxResults(pageSize).addOrder(Order.desc("aId")); 

Criteria criteria = session.createCriteria(A.class); 
criteria.add(Restrictions.in("aId",filter.list())).addOrder(Order.desc("aId")); 
return criteria.list(); 
Cuestiones relacionadas