Search This Blog

Saturday 15 December 2012

HQL and Dynamic Fetching strategies

We saw in our earlier post that HQL ignores the fetching strategy in our mapping. Consider the below query which fetches an entity with id 1.
public static void testNormalFetch() {
    final Session session = sessionFactory.openSession();
    Query q = session.createQuery("from Entity e where e.id is 1");
    Entity entity = (Entity) q.uniqueResult(); 
    System.out.println(" entity class is " + entity.getClass() 
                        + " and Master class is " + entity.getMaster().getClass()
            + " and the collection class is " + entity.getChildren().getClass()) ;
}
The logs indicate the class of the associations and the queried object.
entity class is class com.model.Entity and Master class is class com.model.Mast
er$$EnhancerByCGLIB$$4a6f4cfe and the collection class is class org.hibernate.co
llection.PersistentSet
I also viewed the object in my debug window
As the associations were lazy, hence by default they were not loaded. Separate select queries would be fired to load the same. If the query result had yielded n entity objects consider the number of queries that would be forced to just load even one of the associations. The above scenario is an N+1 query scenario which needs to be resolved to avoid performance bottlenecks.
One option is to change the fetch attribute in the hbm file to make the association non-lazy. However this would result in the associations being loaded at all times which is again a problem.
The solution is to apply a dynamic fetching strategy for the association for the concerned query only. Thus in all other places the code runs just like before while in our query the appropriate fetching strategy is applied.
public static void testFetch() {
    final Session session = sessionFactory.openSession();
    Query q = session.createQuery("from Entity e left join fetch e.children where e.id > 1");
    List<Entity> entity = q.list();
    System.out.println(entity);
}
The logs indicate not juts a select on entity columns but also on columns of the entity_child table.
Hibernate: 
    /* 
from
    Entity e 
left join
    fetch e.children 
where
    e.id > 1 */ 
    select
        entity0_.ID as ID0_0_,
        children1_.ID as ID2_1_,
        entity0_.NAME as NAME0_0_,
        entity0_.DATE as DATE0_0_,
        entity0_.MASTER_ID as MASTER4_0_0_,
        children1_.`KEY` as KEY2_2_1_,
        children1_.ENTITY_ID as ENTITY3_2_1_,
        children1_.ENTITY_ID as ENTITY3_0__,
        children1_.ID as ID0__ 
    from
        ENTITY entity0_ 
    left outer join
        CHILD_ENTITY children1_ 
            on entity0_.ID=children1_.ENTITY_ID 
    where
        entity0_.ID>1
[[Entity] : ( id 2 , data : entity2 , master.Id : 1 , date : 2012-10-10 12:20:20
.0 )], [Entity] : ( id 3 , data : entity3 , master.Id : 1 , date : 2011-10-10 15
:20:20.0 )]]
Interestingly if we omit the fetch word from the query, than the result returned by Hibernate is not a list of entities but a list of object array with the first being an entity object and the second a child object
public static void testWoFetch() {
    final Session session = sessionFactory.openSession();
    Query q = session.createQuery("from Entity e left join  e.children where e.id > 1");
    List<Object[]> recs = q.list();
    for (Object[] rec : recs) {
        for (Object object : rec) {
            System.out.println("object is " + object + ", Class : " 
                 + (null != object ? object.getClass(): null));                
        }
    }
}
Logs:
Hibernate: 
    /* 
from
    Entity e 
left join
    e.children 
where
    e.id > 1 */ 
    select
        entity0_.ID as ID0_0_,
        children1_.ID as ID2_1_,
        entity0_.NAME as NAME0_0_,
        entity0_.DATE as DATE0_0_,
        entity0_.MASTER_ID as MASTER4_0_0_,
        children1_.`KEY` as KEY2_2_1_,
        children1_.ENTITY_ID as ENTITY3_2_1_ 
    from
        ENTITY entity0_ 
    left outer join
        CHILD_ENTITY children1_ 
            on entity0_.ID=children1_.ENTITY_ID 
    where
        entity0_.ID>1
object is [Entity] : ( id 2 , data : entity2 , master.Id : 1 , date : 2012-10-10
 12:20:20.0 )], Class : class com.model.Entity
object is [Child] : ( id 2 , key : 1001 , parent.Id : 2 )], Class : class com.mo
del.Child
object is [Entity] : ( id 3 , data : entity3 , master.Id : 1 , date : 2011-10-10
 15:20:20.0 )], Class : class com.model.Entity
object is null, Class : null
The query generated by Hibernate in both cases is the same. However the result returned by the list method is different.
In the earlier method, the FETCH keyword appears in the FROM clause and indicates that the specified association must be eagerly fetched. This ensures Hibernate does not treat the association as a part of the select clause. Thus only the Entities were returned.
Once the FETCH Keyword was removed, the query was treated as a normal JOIN with all members in the FROM clause being treated as PROJECTION elements. Hence the Child and Entity objects for every row in the result set were returned giving us a list of arrays of objects.

No comments:

Post a Comment