e-Zest members share technology ideas to foster digital transformation.

Hibernate Performance Optimization (Part – 2)

Written by Neetesh Kadam | Aug 22, 2012 3:37:18 PM

Hibernate collection can be eager or lazy fetched which decides whether to initialize association immediately or to defer its initialization till the collection is accessed. Hibernate provides different options (fetching strategies) which decides the queries which hibernate generates to load associated collection.

Hibernate by default uses "select" fetching strategy. That means when you iterate through the collection one by one in persistent context then as soon as you access non-identifier field; hibernate will execute select query to load that item in collection. So in this way if there are 100 items in collection then for each item hibernate will fire 100 select queries to load associations. This may be useful for some use cases but many times hitting database 100 times for loading single collection is not desirable. Our aim is to reduce database hits.

Hibernate provides different options to reduce these database hits. Consider the example of Department entity which has collection of employee objects.

  1. Select Fetch:
  2. So consider the example of Department where we have set of employees with fetchMode Select.

    @OneToMany(fetch=FetchType.LAZY)
    @Fetch(FetchMode.SELECT)
    private Set<Employee> employees;
    

    If there are 10 departments and 10 employees in each department then with this fetch mode hibernate will generate 11 select statements. Check the following code

    DetachedCriteria criteria = DetachedCriteria.forClass(Department.class);
    criteria.add(Expression.like("name", "Dept%"));
    criteria.setResultTransformer(DetachedCriteria.DISTINCT_ROOT_ENTITY);
    List<Department> depts = getHibernateTemplate().findByCriteria(criteria);
    

    After completion of last line hibernate will load departments with name like Dept*. Since we have employee collection which is lazy, hibernate will just instantiate proxy for this and it will not generate any select to load employee collection. The select statement generated by hibernate will look something like

    select * from Department where deptName like "Dept%"
    

    and now if you iterate through employee set and access non-identifier property, like following code.

    for(Department department: depts)
    {
    department.getEmployees().iterator().next().getName();
    }
    

    then following queries will be generated

    select * from Employee emp inner join Department dept on emp.deptId = dept.id where dept.id = 1;
    select * from Employee emp inner join Department dept on emp.deptId = dept.id where dept.id = 2;
    select * from Employee emp inner join Department dept on emp.deptId = dept.id where dept.id = 3;
    so on....
    

    That means if there are 100 departments then 100 select will be generated with the execution of above code.

  3. Batch Fetch:
  4. @OneToMany(fetch=FetchType.LAZY)
    @BatchSize(size=2)
    private Set<Employee> employees;
    

    Mentioning batch size will cause hibernate to load entities in batch; so here queries will be generated something like this

    select * from Department where deptName like "Dept%"
    select * from Employee emp inner join Department dept on emp.deptId = dept.id where dept.id in (1,2)
    select * from Employee emp inner join Department dept on emp.deptId = dept.id where dept.id in (3,4)
    so on....
    

    This batch fetching is used to optimize runtime memory allocation, so that entire collection doesn't gets loaded at once. So by mentioning batch size to 2, number of select gets reduced to 50 (assuming we have 100 departments.)

  5. Subselect Fetch:
  6. @OneToMany(fetch=FetchType.LAZY)
    @Fetch(FetchMode.SUBSELECT)
    private Set<Employee> employees;
    

    With this fetch mode hibernate will generate 2 select statements.

    select * from Department where deptName like "Dept%"
    select * from Employee emp inner join Department dept on emp.deptId = dept.id where dept.id in (select id from Department where deptName like "Dept%")
  7. Join Fetch:
  8. @OneToMany(fetch=FetchType.LAZY)
    @Fetch(FetchMode.JOIN)
    private Set&lt;Employee&gt; employees;
    

    This is as good as eager fetch. Here hibernate uses JOIN to fetch data from department and employee tables. Query generated will be something like this

    select * from Department dept inner join department_employee dept_emp on dept.id=dept_emp.id inner join employee emp on dept_emp.id= emp.id  where deptName like "Dept%"
    

    Instead of fetch type lazy if eager fetch is used, then base on fetch mode hibernate will generate select statement without waiting for the code to access collection.

Conclusion:

Selecting fetch mode wisely will help you to reduce number of database hits. Each fetch strategy has its own advantages and disadvantages. ‘Select’ is a default fetch strategy, you need to explicitly mention fetch strategy to improve performance.