As we saw earlier the Persistence Cache holds a collection of loaded persistent objects. For every object that we load in Hibernate a separate copy (called a snapshot) is maintained within the persistence cache. This copy is used by Hibernate to mange dirty checks.(It is also used as a first level cache.) A downside to this phenomenon is that if we load a very huge object graph into the session there is a possibility that we might run into an OutOfMemoryException.
One of the times this could be avoided is when we load objects from queries. If the reason for the fetch is to simply read them, then the snapshot created is simply a waste of space. A prime example is data loaded from Master tables. This data is never modified, and hence the snapshot in this case is completely irrelevant from the dirty check perspective. So although we need these objects for the repeatable reads, we do not need them to be dirty checked.
Also once we are sure that we wont be needing a particular entity, it would be useful if we could get rid of it from the persistence context.
Hibernate provides us with methods that helps manage the memory consumption for these objects within the persistence cache and also gain some performance improvements.
One option is use the session.readOnly(entity,true) method. This tells Hibernate that the loaded object is a read-only object and hence the snapshot is avoided. We can re-enable dirty checking for this object by calling the same method for the same object with value false.
If a particular set of objects have been operated on successfully and are not necessary needed within the session(i.e. all operations like update/insert are done and flushed) then the objects can be removed from the session using a session.evict(object) call. If all objects need to be cleared from the persistence cache then a session.clear() call can be used.
Consider the below sample code:
On loading the entity with id 3(read-only):
One of the times this could be avoided is when we load objects from queries. If the reason for the fetch is to simply read them, then the snapshot created is simply a waste of space. A prime example is data loaded from Master tables. This data is never modified, and hence the snapshot in this case is completely irrelevant from the dirty check perspective. So although we need these objects for the repeatable reads, we do not need them to be dirty checked.
Also once we are sure that we wont be needing a particular entity, it would be useful if we could get rid of it from the persistence context.
Hibernate provides us with methods that helps manage the memory consumption for these objects within the persistence cache and also gain some performance improvements.
One option is use the session.readOnly(entity,true) method. This tells Hibernate that the loaded object is a read-only object and hence the snapshot is avoided. We can re-enable dirty checking for this object by calling the same method for the same object with value false.
If a particular set of objects have been operated on successfully and are not necessary needed within the session(i.e. all operations like update/insert are done and flushed) then the objects can be removed from the session using a session.evict(object) call. If all objects need to be cleared from the persistence cache then a session.clear() call can be used.
Consider the below sample code:
public static void testPCSize() { Session session = sessionFactory.openSession(); Transaction transaction = session.beginTransaction(); Entity entity1 = (Entity) session.load(Entity.class, 1); System.out.println("Session after loading 1 entity " + session); Entity entity2 = (Entity) session.load(Entity.class, 2); System.out.println("Session after loading 2 entities " + session); System.out.println("Data is " + entity1.getData() + " " + entity2.getData()); System.out.println("Session after complete loading of 2 entities " + session); session.evict(entity1); System.out.println("Session after evicting 1 entity " + session); Entity entity3 = (Entity) session.get(Entity.class, 3); session.setReadOnly(entity3, true); System.out.println("Session after loading a read-only entity " + session); session.clear(); System.out.println("Session after clear " + session); transaction.commit(); }The logs indicate the following:
2765 [main] DEBUG org.hibernate.event.def.DefaultLoadEventListener - loading en tity: [com.model.Entity#1] 2765 [main] DEBUG org.hibernate.event.def.DefaultLoadEventListener - creating n ew proxy for entity Session after loading 1 entity SessionImpl(PersistenceContext[entityKeys=[],coll ectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreat ions=[] collectionRemovals=[] collectionUpdates=[]]) 2781 [main] DEBUG org.hibernate.event.def.DefaultLoadEventListener - loading en tity: [com.model.Entity#2] 2781 [main] DEBUG org.hibernate.event.def.DefaultLoadEventListener - creating n ew proxy for entity Session after loading 2 entities SessionImpl(PersistenceContext[entityKeys=[],co llectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCre ations=[] collectionRemovals=[] collectionUpdates=[]])The two load calls did not actually load any data from the database. It only created proxies. Hence no snapshot found in the persistence cache
2781 [main] DEBUG org.hibernate.impl.SessionImpl - initializing proxy: [com.mod el.Entity#1]
2781 [main] DEBUG org.hibernate.event.def.DefaultLoadEventListener - attempting to resolve: [com.model.Entity#1] 2781 [main] DEBUG org.hibernate.event.def.DefaultLoadEventListener - object not resolved in any cache: [com.model.Entity#1] 2781 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister - Fetc hing entity: [com.model.Entity#1] 2781 [main] DEBUG org.hibernate.loader.Loader - loading entity: [com.model.Enti ty#1] 2781 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - about to open PreparedSt atement (open PreparedStatements: 0, globally: 0) 2796 [main] DEBUG org.hibernate.SQL - select entity0_.id as id0_0_, entity0_.DATA as DATA0_0_ from Entity entity0_ where entity0_.id=? 2937 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - about to open ResultSet (open ResultSets: 0, globally: 0) 2937 [main] DEBUG org.hibernate.loader.Loader - processing result set 2937 [main] DEBUG org.hibernate.loader.Loader - result set row: 0 2937 [main] DEBUG org.hibernate.loader.Loader - result row: EntityKey[com.model .Entity#1] 2968 [main] DEBUG org.hibernate.loader.Loader - done processing result set (1 r ows) 2968 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - about to close ResultSet (open ResultSets: 1, globally: 1) ... mergeCase2sdsadWhen the code tried to work with the actual fields in the two objects, Hibernate searched for their presence in the persistence cache. On not finding the first object (Entity with id 1), it executed a query to fetch the first object and then added it to the cache. The same steps were repeated for the second object.The persistence cache has now loaded two objects in it.
Session after complete loading of 2 entities SessionImpl(PersistenceContext[enti tyKeys=[EntityKey[com.model.Entity#1], EntityKey[com.model.Entity#2]],collection Keys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[ ] collectionRemovals=[] collectionUpdates=[]])Next the code decides to evict an object.
2984 [main] DEBUG org.hibernate.event.def.DefaultEvictEventListener - evicting [com.model.Entity] Session after evicting 1 entity SessionImpl(PersistenceContext[entityKeys=[Entit yKey[com.model.Entity#2]],collectionKeys=[]];ActionQueue[insertions=[] updates=[ ] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] ])The persistence cache now does not hold Entity with id 1. An important thing to keep in mind here is that Entity 1 is not in the first level cache anymore. So if we need Entity with id 1 again, the session will have to issue a select call to get that particular object.
On loading the entity with id 3(read-only):
3000 [main] DEBUG org.hibernate.loader.Loader - done entity load Session after loading a read-only entity SessionImpl(PersistenceContext[entityKe ys=[EntityKey[com.model.Entity#2], EntityKey[com.model.Entity#3]],collectionKeys =[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] co llectionRemovals=[] collectionUpdates=[]])The call to clear is executed after this.
Session after clear SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=
[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] col
lectionRemovals=[] collectionUpdates=[]])
No comments:
Post a Comment