Over a million developers have joined DZone.

Effective Testing around Hibernate/ORM layer

DZone's Guide to

Effective Testing around Hibernate/ORM layer

· DevOps Zone ·
Free Resource

Atomist is your platform for self-service software delivery. Try it for free today!

As applications that use Hibernate or any ORM frameworks grow complex, lots of performance issues don't get enough attention until it is too late. This article accounts for SLA time as part of the junits and allows developers to think in terms of calculating the number of calls hibernate is going to make in the background. This allows for developers to deal with facts rather than assumptions. 

Assuming we are doing TDD, let's build up the TestCase first.
We are going to build up a typical API to create Person with bunch of addresses.
To understand how this works, we will create a buggy Service and include background Audit mechanism, to simulate abstraction to developer.

public class PersonServiceTest {
  private PersonService personService;

  public void insertData() {

  public void cleanup() {

  @Test(timeout = 500)
  public void testAddressCount() {
    assertThat(QueryCountHolder.getGrandTotal().getTotalNumOfQuery(), is(0));
    assertThat(personService.getAddressCount("John"), is(2));
    assertThat(QueryCountHolder.getGrandTotal().getTotalNumOfQuery(), is(1));

  @Test(timeout = 500)
  public void testUpdatePerson() throws Exception {

    assertThat(QueryCountHolder.getGrandTotal().getTotalNumOfQuery(), is(0));

    Person person = personService.getCustomerByName("John");
    assertThat(person.getGender(), CoreMatchers.nullValue());

    assertThat(QueryCountHolder.getGrandTotal().getTotalNumOfQuery(), is(3));

    person = personService.getCustomerByName("John");
    assertThat(person.getGender(), CoreMatchers.is("Male"));

testAddressCount - Based on the service interface, this test case assume 1 sql is executed to get the count of addresses give a first name 
testUpdatePerson - We update the person gender, and assume 3 sqls are executed.
  1. To read the person
  2. Update gender
  3. Audit event to record gender change
Both the test fails 
testAddressCount java.lang.AssertionError:  Expected: is <1> got: <2>
testUpdatePerson java.lang.AssertionError:  Expected: is <3> got: <4>

Let's dive deep into the service and see what is wrong.  
public class PersonServiceImpl implements PersonService {
  private PersonRepository repository;

  @Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
  public int getAddressCount(String firstName) {
    List<Person> list = repository.findByFirstNameLike(firstName);
    if (list == null) {
      return 0;
    Person byFirstNameLike = list.get(0);
    return byFirstNameLike == null ? 0 : byFirstNameLike.getAddresses().size();

  @Transactional(readOnly = false, propagation = Propagation.SUPPORTS)
  public Person save(Person person) {
    // un intended operation
    person.setLastName(person.getLastName() + " 1");
    return repository.save(person);

As you can see in getAddressCount, instead of writing a custom hql, the developer did an mistake of reading object and doing a size on the list. The correct way of writing this method is to make use of custom query which does a count(*) on address table. 

Similarly with Save, the test expected to update Gender, but the service was doing something more than expected, triggering a unexpected audit record on last name. 

To demonstrate this article I have made use of datasource-proxy which is very good API to capture sqls that hibernate or any other ORM frameworks execute under the hood. 

Also, I feel like it is very good practice to use @Test(timeout) so we know if some one changes a method later on, and hinder the SLA.

Atomist is your platform for self-service software delivery. Try it for free today!


Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}