Proyecciones complejas de Hibernate

Quiero preguntar, ¿es posible que cree proyecciones de consultas y criterios para más de un nivel de profundidad? Tengo 2 clases modelo:

@Entity  
@Table(name = "person")  
public class Person implements Serializable {
    @Id
    @GeneratedValue
    private int personID;
    private double valueDouble;
    private int valueInt;
    private String name;
    @OneToOne(cascade = {CascadeType.ALL}, orphanRemoval = true)
    @JoinColumn(name="wifeId")
    private Wife wife;
       /*   
        *  Setter Getter    
        */
}


@Entity 
@Table(name = "wife")  
public class Wife implements Serializable {

    @Id
    @GeneratedValue     
    @Column(name="wifeId")
    private int id;
    @Column(name="name")
    private String name;
    @Column(name="age")
    private int age;            
    /*
     *  Setter Getter
     */       
}

Mi API de criterios:

ProjectionList projections = Projections.projectionList(); 
projections.add(Projections.property("this.personID"), "personID");
projections.add(Projections.property("this.wife"), "wife");
projections.add(Projections.property("this.wife.name"), "wife.name");

Criteria criteria = null; 
criteria = getHandlerSession().createCriteria(Person.class); 
criteria.createCriteria("wife", "wife", JoinType.LEFT.ordinal()); 
criterion = Restrictions.eq("wife.age", 19);  
criteria.add(criterion); 
criteria.setProjection(projections);
criteria.setResultTransformer(Transformers.aliasToBean(Person.class)); 
return criteria.list();

y espero poder consultar Person, con criterios específicos para la propiedad de la esposa y un conjunto de resultados de devolución especificado. así que utilicé Proyecciones para obtener el conjunto de resultados de retorno especificado

Quiero ID de persona, se devolverá el nombre (Persona), el nombre (Esposa). cómo debo usar la API, prefiero usar la API de criterios de Hibernate.

Esta vez, utilicé el código anterior para obtener el resultado esperado, pero generará una excepción con un mensaje de error: Exception in thread "main" org.hibernate.QueryException: could not resolve property: wife.name of: maladzan.model.Person, y si mi Restrictions.eq("wife.age", 19); ¿Es correcto obtener una persona que tiene una esposa con 19 como su valor de edad?

Muchas Gracias

preguntado el 24 de agosto de 12 a las 09:08

4 Respuestas

AFAIK no es posible proyectar más de un nivel de profundidad con el transformador aliastobean. Tus opciones son

  • crear un objeto de transferencia de datos (DTO) aplanado
  • llene la Persona resultante en la memoria usted mismo
  • implementar su propio transformador de resultados (similar a la opción 2)

la opción 1 se ve así:

Criteria criteria = getHandlerSession().createCriteria(Person.class)
    .createAlias("wife", "wife", JoinType.LEFT.ordinal())
    .add(Restrictions.eq("wife.age", 19)); 
    .setProjection(Projections.projectionList()
        .add(Projections.property("personID"), "personID")
        .add(Projections.property("name"), "personName")
        .add(Projections.property("wife.name"), "wifeName"));
    .setResultTransformer(Transformers.aliasToBean(PersonWifeDto.class));

return criteria.list();

Respondido 01 Abr '15, 08:04

Escribí el ResultTransformer, que hace esto exactamente. Su nombre es AliasToBeanNestedResultTransformer, compruébalo en gitHub.

Respondido 01 Feb 16, 14:02

Hola Sami Andoni. Usé su AliasToBeanNestedResultTransformer para la creación de objetos anidados, obtengo un objeto anidado como objeto anidado, pero tengo un pequeño problema. Tengo la intención de obtener solo un campo particular en el objeto anidado, solo algunos campos en el objeto principal, pero el resultado es todos los campos en el objeto principal y todos los campos en el objeto anidado como objeto anidado. No sé si su transformador personalizado es capaz de obtener solo un campo en particular, ¿es posible obtener solo un campo en particular en un objeto anidado como objeto anidado? - El codificador

¿Has solucionado este problema? - Jatin Malwal

@JatinMalwal ¿qué problema? - Sami Andoni

Estoy tratando de hacer lo mismo para obtener solo campos particulares usando AliasToBeanNestedResultTransformer. Pero AliasedTupleSubsetResultTransformer no se encuentra cuando intento hacer esto. - Jatin Malwal

Gracias Sami Andoni. Pude usar su AliasToBeanNestedResultTransformer con una modificación menor para adaptarse a mi situación. Lo que encontré fue que el transformador anidado no admitía el escenario en el que el campo está en una superclase, así que lo mejoré para buscar campos de hasta 10 niveles de profundidad en la jerarquía de herencia de clase de la clase en la que está proyectando:

    public Object transformTuple(Object[] tuple, String[] aliases) {

        ...


                if (alias.contains(".")) {
                    nestedAliases.add(alias);

                    String[] sp = alias.split("\\.");
                    String fieldName = sp[0];
                    String aliasName = sp[1];

                    Class<?> subclass = getDeclaredFieldForClassOrSuperClasses(resultClass, fieldName, 1);
...
}

Donde getDeclaredFieldForClassOrSuperClasses() se define de la siguiente manera:

private Class<?> getDeclaredFieldForClassOrSuperClasses(Class<?> resultClass, String fieldName, int level) throws NoSuchFieldException{
    Class<?> result = null;
    try {
        result = resultClass.getDeclaredField(fieldName).getType();
    } catch (NoSuchFieldException e) {
        if (level <= 10){
        return getDeclaredFieldForClassOrSuperClasses(
                resultClass.getSuperclass(), fieldName, level++);
        } else {
            throw e;
        }
    }
    return result;
}

Mi proyección de Hibernate para esta propiedad anidada se veía así:

Projections.projectionList().add( Property.forName("metadata.copyright").as("productMetadata.copyright"));

y la clase en la que estoy proyectando se ve así:

public class ProductMetadata extends AbstractMetadata {
...
}

public abstract class AbstractMetadata {
...   
   protected String copyright;
...
}

Respondido 02 Feb 15, 06:02

¿Escribió un NestedTransformer que puede admitir OneToMany (Collections) ? - El codificador

@Sangdol lo escribió, solo lo mejoré para admitir la proyección en campos desde una clase base hasta 10 niveles de profundidad: tiradorblanco

En lugar de crear Data Transfer Object (DTO)
In projectionlist haga los cambios a continuación y funcionará para usted.

    ProjectionList projections = Projections.projectionList(); 
    projections.add(Projections.property("person.personID"), "personID");
    projections.add(Projections.property("person.wife"), "wife");
    projections.add(Projections.property("wife.name"));

    Criteria criteria = null; 
    criteria = getHandlerSession().createCriteria(Person.class,"person").createAlias("person.wife", "wife"); 
    criterion = Restrictions.eq("wife.age", 19);  
    criteria.add(criterion); 
    criteria.setProjection(projections);
    criteria.setResultTransformer(Transformers.aliasToBean(Person.class)); 
    return criteria.list();

Respondido el 23 de Septiembre de 15 a las 09:09

no funciona es una palabra diferente, proporcione su versión de hibernación y consulta completa - Jubin Patel

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.