2010-01-09 15 views
5

Tengo un Criterios en consultas, que se une a una segunda tabla B, para seleccionar entidades de la tabla A. El problema es que esta consulta devuelve algunas entidades del cuadro A de múltiples veces. Pero necesito que los resultados sean distintos.agregar un grupo, a una hibernación Criterios en consultas sin proyección

Usando Criteria.DISTINCT_ROOT_ENTITY es inútil, becaus este filtra las múltiples apariciones después de la consulta SQL fue ejecutado. Entonces, cuando limito mis resultados a 20 hits, termino con solo 4, aunque hay más entradas, que coinciden con mi consulta.

En SQL puro, simplemente puedo agregar un "GROUP BY ID" a la consulta y todo está bien, porque la unión de la tabla B solo se usa para seleccionar las entidades de la tabla A. Pero con Criteria-API I no puedo hacer esto La única forma de agregar un "GROUP BY" es mediante el uso de proyecciones. Pero luego, termino con valores escalares, no con una instancia real de mi clase. Usar una restricción de SQL tampoco funciona, porque hibernate agrega un bogo "1 = 1" después de mi cláusula "GROUP BY". :(

Algunas ideas?

+0

'GROUP BY', por definición, implica la agregación de datos, por lo que las proyecciones son necesarias. Quizás si agregaste más detalles de las tablas y la consulta SQL que quisieras que genere Hibernate, podríamos aconsejarte mejor. – skaffman

+0

Tengo un evento de clase. Esta clase tiene una lista de fechas. (En realidad, estas fechas son una clase especial de DateWrapper, que envuelve la fecha y agrega una identificación, porque hibernate no puede unir las colecciones de tipo de valor en este momento). Me gustaría consultar los eventos y encontrar todos los eventos con uno o más eventos entre xe y. Cuando escribo SQL-Query, que fue generado por Criteria-API y agrego un "ID de GROUP BY", hace exactamente lo que estoy buscando. ¡Pero no puedo encontrar ninguna manera de argumentar Hibernate para agregar GROUP BY! –

Respuesta

0

Es posible escribir consultas SQL reales que Hibernate puede utilizar para devolver entidades. Así que si realmente necesita, puede omitir HQL y escribir exactamente la consulta que desea con su grupo . pOR en ella

Ver here para más detalles

por ejemplo, puede definir una consulta algo como esto en su archivo hbm.xml:.

<sql-query name="exampleQuery"> 
<query-param name="param" type="int"/> 
<return alias="x" class="foo.bar.X"/> 
<return alias="y" class="foo.bar.Y"/> 
    <![CDATA[ 
     select {x.*}, {y.*} 
     from XEntity x 
     inner join YEntity y on y.xId = x.id 
     where y.value = :param 
    ]]> 
</sql-query> 

Tenga en cuenta que {x. } y {y.} sintaxis abreviada para la selección de todas las propiedades de la entidad X y la entidad Y

+0

Gracias por su respuesta :) pero no estoy usando HQL. Estoy usando Criteria-API y estoy obligado a ello, ¡porque la consulta debe ser compilada dinámicamente! Por lo tanto, necesito encontrar una manera de decirle a Hibernate que agrupe los resultados por la propiedad id, sin usar Proyecciones, porque necesito instancias reales de mis clases como reslut, no como valores escalares. –

+0

También es posible especificar una consulta SQL nativa programáticamente en lugar de una consulta con nombre como se muestra arriba, aunque no mediante el API de criterios. – alasdairg

1

¿Ha intentado usar algo como esto?

 ICriteria criteria = dc.GetExecutableCriteria(RepositoryInstance.Session) 
           .SetProjection(
            Projections.Distinct(Projections.ProjectionList() 
             .Add(Projections.Property("Prop1"),"Prop1") 
             .Add(Projections.Property("Prop2"),"Prop2") 
             .Add(Projections.Property("Prop3"),"Prop3") 
             .Add(Projections.Property("Prop4"),"Prop4"))); 
result = criteria.List(); 

Puede añadir dinámicamente propiedades a través de la reflexión de la clase.

Esto crea SQL como esto: select distinct prop1,prop2,prop3,prop4 from yourClass

no he incluido DetachedCriteria dc ya que eso es irrelevante.

1

GROUP BY SIN PROYECCIÓN: No es posible, ya que tiene sentido, en muchas respuestas que pueden hallarse, pero la mayoría de la gente no quiere utilizar la proyección, ya que les obliga a proyectar todos y cada atributo, pero el requisito es que un frijol debe ser proyectado. (y regresó como resultado). En el siguiente ejemplo he intentado con project el bean requerido como objeto resultante.

He logrado el mismo resultado con un poco de truco. Creo que primero intenté aplicar group by sin proyección pero no encontré ninguna solución, así que tengo que confiar en Projection.

Esto es lo que quería lograr

select p.* FROM parent p INNER JOIN child c ON p.id_parent=c.id_father 
WHERE c.child_name like '%?%' AND p.parent_name like '%?%' 
group by p.id_parent 

en código Java que quería p.* a ser una clase Parent que es mi bean de entidad y quería que sea único, una forma es obtener la lista de resultados de una Establecer, pero no me gusta de esta manera debido a muchas razones :)

Así que creé un Criterio de Child.class en lugar de Parent.class, y este truco funcionó para mí.

Criteria c = session.createCriteria(Child.class,"c");// starting from Child 
    c.add(Restrictions.like("childName", "abc", MatchMode.ANYWHERE)); 
    c.createAlias("parent", "p"); //remember parent is an attribute in Child.class 
    c.add(Restrictions.like("p.parentName", "xyz", MatchMode.ANYWHERE)); 
    c.setProjection(Projections.projectionList().add(Projections.groupProperty("parent"))); //projecting parent which is an attribute of Child.class 

    List<Parent> result = c.list(); //get the result 
    for (Parent p: result) { 
     System.out.println(p); 
    } 

Si todavía no tiene la idea aquí están las clases de Entity Bean mapeadas.

package com.mazhar.beans; 

import static javax.persistence.GenerationType.IDENTITY; 

import java.util.List; 

import javax.persistence.CascadeType; 
import javax.persistence.Column; 
import javax.persistence.Entity; 
import javax.persistence.FetchType; 
import javax.persistence.GeneratedValue; 
import javax.persistence.Id; 
import javax.persistence.OneToMany; 
import javax.persistence.Table; 

@Entity 
@Table(name = "parent") 
public class Parent { 
    private Integer idParent; 
    private String parentName; 
    private List<Child> childs; 

    @Id 
    @GeneratedValue(strategy = IDENTITY) 
    @Column(name = "id_parent") 
    public Integer getIdParent() { 
     return idParent; 
    } 
    public void setIdParent(Integer idParent) { 
     this.idParent = idParent; 
    } 

    @Column(name = "parent_name") 
    public String getParentName() { 
     return parentName; 
    } 
    public void setParentName(String parentName) { 
     this.parentName = parentName; 
    } 

    @OneToMany(fetch=FetchType.LAZY, mappedBy="parent", cascade=CascadeType.ALL) 
    public List<Child> getChilds() { 
     return childs; 
    } 
    public void setChilds(List<Child> childs) { 
     this.childs = childs; 
    } 

} 

y mi clase hija

package com.mazhar.beans; 

import static javax.persistence.GenerationType.IDENTITY; 

import javax.persistence.CascadeType; 
import javax.persistence.Column; 
import javax.persistence.Entity; 
import javax.persistence.FetchType; 
import javax.persistence.GeneratedValue; 
import javax.persistence.Id; 
import javax.persistence.JoinColumn; 
import javax.persistence.ManyToOne; 
import javax.persistence.Table; 

@Entity 
@Table(name = "child") 
public class Child { 
    private Integer idChild; 
    private String childName; 
    private Parent parent; //this actually we projected in criteria query. 

    @Id 
    @GeneratedValue(strategy = IDENTITY) 
    @Column(name = "id_city", unique = true, nullable = false) 
    public Integer getIdChild() { 
     return idChild; 
    } 

    public void setIdChild(Integer idChild) { 
     this.idChild = idChild; 
    } 

    @Column(name = "city_name", nullable = false) 
    public String getChildName() { 
     return childName; 
    } 

    public void setChildName(String cName) { 
     this.childName = cName; 
    } 

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) 
    @JoinColumn(name = "id_father") 
    public Parent getParent() { 
     return parent; 
    } 

    public void setParent(Parent parent) { 
     this.parent = parent; 
    } 
} 
0

El principal problema de la agrupación por sin proyectar, en la forma que desee, es que en algunos DBMS como Oracle no va a funcionar, el Oracle devolverá una error.

Si agrupa una selección, debe agrupar por todos los campos de no agregación que está seleccionando. El MySQL, por ejemplo, no tiene esta restricción.

El enfoque que he estado usando es seleccionar solo el id como proyección de propiedad de grupo con todos los filtros, ordenaciones y límites de resultados. A continuación, ejecute otro filtro de consulta con estos identificadores recuperados. De esta forma, la implementación será independiente del DBMS.

Cuestiones relacionadas