In an earlier post I used the ScrollableResults object along with a session to batch multiple operations. In this case I had to manually flush and clear the session after every n SQL operations to prevent my persistence cache from having a memory overflow.
Hibernate also offers an alternative type of Session where it counts on the user to mange the SQL updates on his own.
In this type of hibernate session, there is no concept of the persistence context. So hibernate is not capable of detecting any automatic changes (dirty-checking) and automatically scheduling updates. As it does not have a cache of objects loaded from the database, the same SQL query fired in one session could result in two different objects in java memory.
All the advantages of the Session Interface like delayed write, first level cache are lost. This interface is known as the StatelessSession
Consider the below example. It is very similar to the one involving Session (except that it doesn't use the Session anymore).
The logs below indicate the absence of any persistence cache in this session. Also the queries are fired immediately when the update calls are fired.
How would this behaviour affect cascade relations ?
I extended my entity class and added a many-to-one-relation in the sub-class to another very simple entity.
Considering all of the above, I would prefer not to use this API, instead sticking to manually flushing and clearing the session for batch updates.
Hibernate also offers an alternative type of Session where it counts on the user to mange the SQL updates on his own.
In this type of hibernate session, there is no concept of the persistence context. So hibernate is not capable of detecting any automatic changes (dirty-checking) and automatically scheduling updates. As it does not have a cache of objects loaded from the database, the same SQL query fired in one session could result in two different objects in java memory.
All the advantages of the Session Interface like delayed write, first level cache are lost. This interface is known as the StatelessSession
Consider the below example. It is very similar to the one involving Session (except that it doesn't use the Session anymore).
static void testEntityBatchUpdate() { final String query = "from Entity where id < :id"; StatelessSession session = sessionFactory.openStatelessSession(); System.out.println("new StatelessSession : " + session); Transaction transaction = null; ScrollableResults allEntities = null; transaction = session.beginTransaction(); allEntities = session.createQuery(query).setLong("id", 7).scroll(); while(allEntities.next()) { Entity entity = (Entity) allEntities.get(0);//This object is in detached state System.out.println("StatelessSession after an entity fetch : " + session); System.out.println("Updating entity with id - " + entity.getId() + " current name is " + entity.getName()); //Detached object updated entity.setName("Updated new Name " + entity.getId()); session.update(entity);//Will not make object persistent again //- instead fires an SQL update immediately } transaction.commit(); session.close(); }The Stateless session directly executes statements on the database based on the methods we call. It is very much like JDBC. As the objects are no longer updated based on dirty-checks, we need to explicitly call the update method. The object state that we learned earlier also does not apply to these objects. Whereas in case of a session our modified objects would have been in the persistent state, here the objects returned from the SQL select query are all in the detached state.(Hence no dirty checking and automatic save.) On calling the update method the object is explicitly updated to the database and then becomes detached again.
The logs below indicate the absence of any persistence cache in this session. Also the queries are fired immediately when the update calls are fired.
3063 [main] DEBUG org.hibernate.SQL - select entity0_.ID as ID0_, entity0_.NAME as NAME0_ from ENTITY entity0_ where entity0_.ID<? StatelessSession after an entity fetch : org.hibernate.impl.StatelessSessionImpl @4c4975 3156 [main] DEBUG org.hibernate.engine.TwoPhaseLoad - done materializing entity [com.batch.stateless.Entity#1] StatelessSession after an entity fetch : org.hibernate.impl.StatelessSessionImpl @4c4975 Updating entity with id - 1 current name is wew update ENTITY set NAME=? where ID=? ... StatelessSession after an entity fetch : org.hibernate.impl.StatelessSessionImpl @4c4975 ... Updating entity with id - 2 current name is wew ... update ENTITY set NAME=? where ID=?As we are working with detached objects would I be able to navigate the object graph?
How would this behaviour affect cascade relations ?
I extended my entity class and added a many-to-one-relation in the sub-class to another very simple entity.
public class ComplexEntity extends Entity { private Master master; //setter-getters }
public class Master { private Long id; private String data; //setter-getters }The hbm file for ComplexEntity includes the mapping:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.batch.stateless"> <class name="ComplexEntity" table="COMPLEX_ENTITY"> <id name="id" column="ID" type="long"> <generator class="identity" /> </id> <property name="name" type="string"> <column name="NAME" /> </property> <many-to-one name="master" class="Master" foreign-key="COMPLEX_ENTITY_FK1"> <column name="MASTER_ID"></column> </many-to-one> </class> </hibernate-mapping>I then tried to navigate the object graph to read the fields via the Master association.
static void testComplexEntityBatchUpdate() { final String query = "from ComplexEntity where id < :id"; StatelessSession session = sessionFactory.openStatelessSession(); System.out.println("new StatelessSession : " + session); Transaction transaction = null; ScrollableResults allEntities = null; transaction = session.beginTransaction(); allEntities = session.createQuery(query).setLong("id", 3).scroll(); while(allEntities.next()) { ComplexEntity cEntity = (ComplexEntity) allEntities.get(0); System.out.println("StatelessSession after an entity fetch : " + session); System.out.println("Updating entity with id - " + cEntity.getId() + " current name is " + cEntity.getName() + " and Master data is " + cEntity.getMaster().getData()); cEntity.setName("Updated new Name " + cEntity.getId()); session.update(cEntity); } transaction.commit(); session.close(); }On executing the code:
2766 [main] DEBUG org.hibernate.SQL - select complexent0_.ID as ID0_, complexent0_.NAME as NAME0_, complexent0_.MASTER_ID as MASTER3_0_ from COMPLEX_ENTITY complexent0_ where complexent0_.ID<? ... 2813 [main] DEBUG org.hibernate.engine.TwoPhaseLoad - done materializing entity [com.batch.stateless.ComplexEntity#1] 2813 [main] DEBUG org.hibernate.engine.StatefulPersistenceContext - initializin g non-lazy collections StatelessSession after an entity fetch : org.hibernate.impl.StatelessSessionImpl @1315d34 ... Exception in thread "main" org.hibernate.SessionException: proxies cannot be fet ched by a stateless session at org.hibernate.impl.StatelessSessionImpl.immediateLoad(StatelessSessionImpl.j ava:220) at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializ er.java:66) at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyIn itializer.java:111) at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.invoke(CGLIBLazyInitiali zer.java:150) at com.batch.stateless.Master$$EnhancerByCGLIB$$2efcc8d1.getData(<generated>) at com.batch.stateless.TestUpdate.testComplexEntityBatchUpdate(TestUpdate.java: 58) at com.batch.stateless.TestUpdate.main(TestUpdate.java:18)
Being in the detached state Hibernate is unable to navigate the association, resulting in an exception. To make this work we would need to remove the lazy loading for this relation.
For cascade operations, modifications made to collections would be ignored (unless it was a collection of entities) Also any interceptors defined would be bypassed by the stateless-session.Considering all of the above, I would prefer not to use this API, instead sticking to manually flushing and clearing the session for batch updates.
When you are using hibernate stateless session, then hibernate does not use any cache( first level, or second level) even does not maintain transaction , no proxy object. Stateless session is useful when you have thousands of records to fetch and you have very less memory which causes out of memory error.
ReplyDelete