Optimizing fetches in hibernate

Optimizing fetches 

(the next article will be on optimizing updates)

This helps in case of one to many, or many to many entity relationship. If one has lazy loading turned on, typically hibernate load the actual child (many end of entity relation) only when the get method is called. This is behavior is desired in most cases. The downside is very time the get method is involves, we make second call to database to fetch that entity (if it no already in hibernate cache).

I take simple example for illustration, we have user and address entities, user can have one or more addresses.

I have this code

Query query = session.createQuery(“from User”);
List<User> users = (List<User>)query.list();

for (User user : users) {
List<Address> addresses = user.getAddress();
for (Address address :addresses) {

// do some processing the on the address.

}
}

If I have show sql property turned on in hibernate i see following

Hibernate: select user0_.id as id0_, user0_.email_address as email2_0_, user0_.last_access_time as last3_0_, user0_.login_name as login4_0_, user0_.password as password0_, user0_.registration_date as registra6_0_, user0_.verified as verified0_ from users user0_

Hibernate: select address0_.user_id as user4_1_, address0_.id as id1_, address0_.id as id1_0_, address0_.address as address1_0_, address0_.index1 as index3_1_0_, address0_.user_id as user4_1_0_ from address address0_ where address0_.user_id=?


‘ repeat till all address related to all users are read.


Hibernate: select address0_.user_id as user4_1_, address0_.id as id1_, address0_.id as id1_0_, address0_.address as address1_0_, address0_.index1 as index3_1_0_, address0_.user_id as user4_1_0_ from address address0_ where address0_.user_id=?

This is not optimal in this case, think of how many times we will be going to database to fetch all the addresses.

One way to optimize this to use batch size annotation,

@Cascade( {CascadeType.ALL, CascadeType.DELETE_ORPHAN})
@OneToMany(mappedBy = “user”, fetch = FetchType.LAZY)
@JoinColumn(name=”user_id”)
@org.hibernate.annotations.BatchSize(size=4) // added this annotation later.
public List<Address> getAddress() {
return address;
}

The same code now executes with following output

Hibernate: select user0_.id as id0_, user0_.email_address as email2_0_, user0_.last_access_time as last3_0_, user0_.login_name as login4_0_, user0_.password as password0_, user0_.registration_date as registra6_0_, user0_.verified as verified0_ from users user0_

Hibernate: select address0_.user_id as user4_1_, address0_.id as id1_, address0_.id as id1_0_, address0_.address as address1_0_, address0_.index1 as index3_1_0_, address0_.user_id as user4_1_0_ from address address0_ where address0_.user_id in (?, ?, ?, ?)
Hibernate: select address0_.user_id as user4_1_, address0_.id as id1_, address0_.id as id1_0_, address0_.address as address1_0_, address0_.index1 as index3_1_0_, address0_.user_id as user4_1_0_ from address address0_ where address0_.user_id in (?, ?, ?, ?)

So in this case, we go fetch addresses for 4 user ids at time. 4 because our fetch size is 4. I have this at individual entity to entity mapping level, you can also set it globally using hibernate property

<prop key=”hibernate.default_batch_fetch_size”>4</prop>

I would advise when performing this at global level, make sure this does not have adverse effect your applications memory foot print.

Advertisements

One thought on “Optimizing fetches in hibernate

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s