Search This Blog

Sunday, 19 February 2012

First Level Cache and Repeatable Reads

As we saw earlier, with every hibernate session, a persistence context is associated. This context is aware of all the persistent objects that are associated with the hibernate session. The objects are maintained in maps with the identifier as the primary key. Hibernate very smartly uses this data to perform some query optimization.
Consider the below code that retrieves record from the database.
public static void testFetch() {
    Session session = sessionFactory.openSession();
    System.out.println("Fetching the object via load call");
    Entity entity1 = (Entity) session.load(Entity.class, 1);
    String data = entity1.getData();
    System.out.println("Fetching the object via get call");
    Entity entity2 = (Entity) session.load(Entity.class, 1);
    System.out.println("Objects are equal ? " + entity2.equals(entity1));
    session.close();
}
The output for the equals check is
Fetching the object via load call
2984 [main] DEBUG org.hibernate.event.def.DefaultLoadEventListener  - loading en
tity: [com.model.Entity#1]
...
Fetching the object via get call
2984 [main] DEBUG org.hibernate.event.def.DefaultLoadEventListener  - loading en
tity: [com.model.Entity#1]
2984 [main] DEBUG org.hibernate.event.def.DefaultLoadEventListener  - entity pro
xy found in session cache
...
Objects are equal ? true
From the logs we can also see that the select query was executed only once.
2625 [main] DEBUG org.hibernate.SQL  - 
    select
        entity0_.id as id0_0_,
        entity0_.DATA as DATA0_0_ 
    from
        Entity entity0_ 
    where
        entity0_.id=?
Also the equals method returned true. This tells us two things:
  1. For any record in the database only a single representation is present in Hibernate session.
  2. If the record is already present in Hibernate session, the object is not fetched again, it is simply used from the persistence context.
This second advantage is what we call a repeatable read. The repeatable read works for queries executed using Hibernate's query object and also for Hibernate Criteria queries. For any query result, Hibernate tries to resolve the object in the persistence cache. The below method will also return the two objects as equal.
public static void testFetch() {
    Session session = sessionFactory.openSession();
    System.out.println("Fetching the object via load call");
    Entity entity1 = (Entity) session.load(Entity.class, 1);
    String data = entity1.getData();
    System.out.println("Fetching the object via query call");
    Query q = session.createQuery("from Entity e where e.id = ?");
    q.setInteger(0, 1);
    Entity entity2 = (Entity) q.uniqueResult();
    System.out.println("Objects are equal ? " + entity2.equals(entity1));
    session.close();
}
For the query Hibernate reads the id from the result-set and tries to resolve this object in the persistence cache.Only if the record is not found thus Hibernate load the remaining data from the result-set.Thus before loading any object, Hibernate checks if the object is already present in the session. If it finds it in the persistence context it avoids the data fetch, instead returning the object in the persistence context.
As a result, the session is now working as a cache of database objects. This cache works across transactions. As sessions are associated with a single thread we do not encounter any concurrency issues.
As every record is there only once in a session, any circular references (possible in bidirectional associations) do not lead to a stack overflow.

No comments:

Post a Comment