Search code examples
javaspringhibernatehqlpojo

Map hibernate projections result to java POJO model


I've been using spring and hibernate for this past few weeks and I've always been learning something new there.

Right now I've got a problem that I want to solve with Projections in Hibernate.

Suppose there is a model Person and that model has many Car. The following are how the class definitions roughly gonna look like:

public class Person implements java.io.Serializable {
    private Integer id;
    private String name;
    private List<Car> cars;
    private Integer minYear; // Transient
    private Integer maxYear; // Transient
}

public class Car implements java.io.Serializable {
    private Integer id;
    private Integer year;
}

The problem here is I want to get the minYear (maxYear) of each Person to be filled by the earliest year (latest year) of the cars they have.

Later I found a solution to use Projections but I stumbled upon org.hibernate.QueryException: could not resolve property: minYear of: model.Person and here is the code of the db operation:

Criteria criteria = sessionFactory.getCurrentSession().createCriteria("model.Person");
            criteria.add(create(personInstance));
            criteria.createAlias("minYear", "minYear");
            criteria.setProjection(Projections.min("cars.year").as("minYear"));

Is there anyway to store the aggregation value in transient method using Projections because I just want to avoid using plain SQL and HQL as much as possible.


Solution

  • Never mind, I've found the solution.

    1. First we need to create alias of the associated object like so

      Criteria criteria = sessionFactory.getCurrentSession().createCriteria("model.Person");
      criteria.createAlias("cars", "cars");
      
    2. Select the needed using Hibernate Projections

      ProjectionList projections = Projections.projectionList();
      projections.add(Projections.property("id").as("id"));
      projections.add(Projections.property("name").as("name"));
      projections.add(Projections.property("cars").as("cars"));
      
    3. Group the result based on the root entity (in this case using its id, Person.id), this is needed especially when used with aggregation to group the aggregation

      projections.add(Projections.groupProperty("id"));
      
    4. Use the aggregate function

      projections.add(Projections.min("cars.year").as("minYear"));
      projections.add(Projections.max("cars.year").as("maxYear"));
      
    5. Set the projection

      criteria.setProjection(projections);
      
    6. Use result transformer AliasToBeanResultTransformer to map the result fields (as specified in step 2 & 4) to the POJO

      criteria.setResultTransformer(new AliasToBeanResultTransformer(Person.class));
      
    7. Get the result

      List<Person> results = (List<Person>) criteria.list();