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.
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.
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.
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 windowAs 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 : nullThe 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