Showing posts with label Hibernate. Show all posts
Showing posts with label Hibernate. Show all posts

Tuesday, 20 March 2012

Hibernate Caching

Hibernate provides three different caching mechanisms; first-level, second-level and query cache. Understanding how to use the caching mechanisms is important to enhance performance. Incorrectly configuring caching could lead to degrading performance. This post gives a conceptual understanding of how Hibernate caching works. 

The configuration of caching is provided by the Hibernate documentation.

First-level Cache
The Hibernate Session is a unit of work representing a transaction at the database level. When a session is created and Hibernate entities modified, Hibernate will not update the underlying database tables immediately. Instead it will keep track of the changes and perform a reduced number of SQL statements at the end of the session. For example, if an entity is modified several times within the same session, Hibernate will generate only one SQL update statement at the end of the session containing all the changes.

Second-level Cache 
The second-level cache is associated with the SessionFactory rather than each Session and it is not enabled by default. The second-level cache doesn't store instances of an entity (to prevent trips to the database when the entity is requested); rather it stores a dehydrated state of the entity. Conceptually this can be thought of as a Map which contains the entity's id as the key and and an array of the properties as value. 

As an example lets assume we have the following Employee entity:


public class Employee {
  private Employee manager;
  private String forename;
  private String surname;
  private Set<Employee> staff;
  //setters and getters
}

Hibernate will cache the records as such:

Conceptual Employee Data Cache

Id  [forename, surname, manager, [staff] ]
1 [ “John”, “Smith”, null, [2, 3] ]
2 [“Sarah”,”Brown”, 1, [] ]
3 [“Gavin”, “Adams” 1, [] ]


So if the Employee with id 1 is queried from the database without the cache, it would result in the following queries:


select * from Employee where id=1 ; load the employee with id 1
select * from Employee where manager_id=1 ; load the staff of 1 (will return 2, 3)
select * from Employee where manager_id=2 ; load any potential staff of 2 (will return none)
select * from Employee where manager_id=3 ; load any potential staff of 3 (will return none)


With the cache enabled, there would be no SQL select statements executed. If however, the associations were not cached then it would result in all the queries except the first. Therefore, it is best to cache associations whenever possible. 

The above queries were based on using the entity identifier. If the query were more complex such as by forename then Hibernate must still issue a select statement to retrieve the identifier of the entity before the cache can be queried for associations.
 
//Complex query
Query query = session.createQuery("from Employee as e where e.forename=?");
query.setString(0, "John");
List l = query.list();

//single SQL select statment to retrieve id.
select * from Employee where forename='John'

This mandatory select statement to retrieve the id is where the query cache can be used.

Query Cache
The query cache is responsible for caching queries and their results. This is only useful for queries that are run frequently with the same parameters. Conceptually the query cache works similarly to the caching of associations in the second-level cache; the query and parameters are stored as a key, with the value being a list of identifiers for that query. These identifiers are then used to query the second-level cache for a given entity which is then hydrated.

Wednesday, 13 July 2011

Error Solution: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role; no session or session was closed

The problem is when I run the test, it failed on the last assert with the “org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role; no session or session was closed” error.

My DAO method and its test is as follows:


DAOImpl:
public List getActivityUIFields(ActivityType activityType) {
List rv = null;
try {
Query query = em.createNamedQuery("mobileWallet.uiConfigDAO.getUIConfigData");
query.setParameter("activityType", activityType.getValue());
rv = query.getResultList();
} catch (NoResultException nre) {
// No record found.
}
return rv;
}

DAOImplTest:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:/dao-test.xml" })
@Transactional
public class UIConfigDAOImplTest {
@Autowired
private EntityManagerFactory entityManagerFactory;

private EntityManager em;

@Autowired
private UIConfigDAO uiConfigDAO;

@Before
public void setUp() {
em = entityManagerFactory.createEntityManager();
}

@After
public void tearDown() throws Exception {
}

@Test
public void testGetUIConfigData() {

List list = uiConfigDAO.getActivityUIFields(ActivityType
.getInstance("SELF"));
Assert.assertNotNull(list);
Assert.assertFalse(list.isEmpty());
Assert.assertEquals("FirstName", list.iterator().next().getValue());
Assert.assertNotNull(list.iterator().next().toString());
}
}


Its bit surprising that Assert.assertEquals("FirstName", list.iterator().next().getValue()); got passed but test failed on the next line.

Reason: Once the Hibernate Session is closed anything that hasn't been loaded, can't be loaded without reattaching your entity to a Hibernate Session. The Session will end once the statement has been executed. If I debug the test method, list has all the values and it works if getValue() is called. But fails on accessing the collection.

Solution: The whole test should be running into a transaction. The easiest way is adding @Transactional at class level. This will put the whole test method in one transaction.

We faced the similar situation when business service was called and the reason was the business service was not under one transaction. The method should be in a transaction where the retrieved collection is used. Always keep the business service in a transaction.

Wednesday, 6 July 2011

Transaction demarcation

Should the transaction be demarcated at the service layer or DAO layer? The best or normal practice is to mark the service layer operation as transactional since this will represent the single unit-of-work (or use-case) that we would like to execute in a all-or-nothing manner. Furthermore, the service operation may invoke multiple DAOs which together may need to work in a single transaction and constitute the unit-of-work.


Reference: This article gives a good discussion: http://stackoverflow.com/questions/1079114/spring-transactional-annotation-best-practice

Use of entityManager flush during persist

We encountered a scenario where persisting some duplicate data correctly caused a data integrity issue and needed to translate that into an application exception. Our exception was declared within the service operation by wrapping the call to the DAO within a try-catch block and catching any exception. During runtime we noticed that our exception wasn’t being thrown. It transpired that as the service operation was marked as transactional, the persisting of the data and execution of the SQL statements was not occurring when the DAO operation completed but after the service operation (i.e. end of the transaction). Hence the constraint violation exception was not being caught within the service operation as had been planned. For cases like this where an exception is required to be caught the solution is to flush the SQL cache immediately after persisting the data.

@Transactional
@Override
public void storeTopupVouchers(List list) throws CreateVoucherException {
  try {
    TopUpDAO.create(list);
  } catch (Exception e) {
    throw new CreateVoucherException("Failed to create voucher -
    "+e.getMessage(),e);
  }
}


@Override
public void create(List list) {
  for(Topup topup : list) {
    em.persist(topup);
  }
    em.flush();
}

Reference: http://stackoverflow.com/questions/4275111/correct-use-of-flush-in-jpa-hibernate