5

I want to ask, it is possible that I create query projections and criterion for more than one level deep? I have 2 model classes:

@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
     */       
}

My Criteria API :

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();

and I hope, I can query Person, with specified criteria for wife property, and specified return resultSet. so i used Projections for getting specified return resultSet

I want personID, name(Person), name(Wife) will returned. how API i must Use, i more prefer use Hibernate Criteria API.

This time, I used code above for getting my expected result, but it will throw Exception with error message : Exception in thread "main" org.hibernate.QueryException: could not resolve property: wife.name of: maladzan.model.Person, and whether my Restrictions.eq("wife.age", 19); is correct for getting person which has wife with 19 as her age value ?

Thanks

Tom11
  • 2,419
  • 8
  • 30
  • 56
Fauzi Achmad
  • 173
  • 1
  • 2
  • 14

4 Answers4

6

AFAIK it is not possible to project more than one level deep with aliastobean transformer. Your options are

  • create a flattened Data Transfer Object (DTO)
  • fill the resulting Person in memory yourself
  • implement your own resulttransformer (similar to option 2)

option 1 looks like this:

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();
Sanghyun Lee
  • 21,644
  • 19
  • 100
  • 126
Firo
  • 30,626
  • 4
  • 55
  • 94
5

I wrote the ResultTransformer, that does this exactly. It's name is AliasToBeanNestedResultTransformer, check it out on github.

Tom11
  • 2,419
  • 8
  • 30
  • 56
Sami Andoni
  • 694
  • 6
  • 7
  • Hi Sami Andoni. I did used your AliasToBeanNestedResultTransformer for nested object creation, I do get nested object as nested object, but I've a small Issue. I intend to get only particular field in nested object, only few fields in parent object, but the outcome is all fields in parent object and all fields in nested object as nested object. I dont know whether your custom transformer is capable for fetching only a particular field, is it possible to fetch only a particular field in nested object as nested object? – The Coder Mar 02 '15 at 06:59
  • Have u solvd this issue? – Jatin Malwal Oct 04 '15 at 15:35
  • @JatinMalwal what issue? – Sami Andoni Oct 04 '15 at 16:39
  • I'm trying to do the same thing to fetch only particular fields using AliasToBeanNestedResultTransformer. But AliasedTupleSubsetResultTransformer is not found when I'm try to make this. – Jatin Malwal Oct 04 '15 at 19:50
1

Thanks Sami Andoni. I was able to use your AliasToBeanNestedResultTransformer with a minor modification to suit my situation. What I found was that the nested transformer did not support the scenario where the field is in a super class so I enhanced it to look for fields up to 10 levels deep in the class inheritance hierarchy of the class you're projecting into:

    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);
...
}

Where getDeclaredFieldForClassOrSuperClasses() is defined as follows:

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;
}

My Hibernate projection for this nested property looked like this:

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

and the class I am projecting into looks like this:

public class ProductMetadata extends AbstractMetadata {
...
}

public abstract class AbstractMetadata {
...   
   protected String copyright;
...
}
whitestryder
  • 451
  • 5
  • 10
-1

Instead of creating Data Transfer Object (DTO)
In projectionlist make below changes and it will work for you.

    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();
Jubin Patel
  • 1,959
  • 19
  • 38