I have the following scenario:
- A stateless session bean
- One method on that SSB.
- The SSB uses CMT and the method TX attribute is set to REQUIRED
- A DAO which is wired using spring and has a transactional proxy
I'm using WebSphere 5.1 and hibernate as ORM tool, and I have two hbm files. One parent.hbm.xml and one child.hbm.xml. The parent has a one to many relation with the child, and the lazy flag is set to true.
The business logic inside the bean method looks like this:
[SSB method]
1. DAO dao = (DAO) beanFactory.getBean("dao");
2. TransferObjectParent parent = dao.find(1);
3. TransferObjectChild child = new TransferObjectChild();
4. parent.addChild(child);
Since lazy is set to true, the parent object returned from the dao is actually a hibernate proxy. From the moment I add the child to it in step 4, hibernate will want to update the child table automatically, adding a child which references its parent using a foreign key.
My application context relating this scenario:
--------------------- Ok, now the problem:Code:<bean id="db" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName"> <value>java:comp/env/jdbc/db</value> </property> </bean> <bean name="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource"> <ref bean="db" /> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.DB2Dialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.jdbc.use_streams_for_binary">true</prop> <prop key="hibernate.max_fetch_depth">1</prop> <prop key="hibernate.use_second_level_cache">false</prop> <prop key="hibernate.use_query_cache">false</prop> </props> </property> <property name="mappingResources"> <list> <value>parent.hbm.xml</value> <value>child.hbm.xml</value> </list> </property> </bean> <bean id="tran" class="org.springframework.transaction.jta.WebSphereTransactionManagerFactoryBean" /> <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"> <constructor-arg> <ref bean="tran" /> </constructor-arg> </bean> <bean name="daoTarget" class="DAO"> <property name="sessionFactory"> <ref bean="sessionFactory" /> </property> </bean> <bean id="dao" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager"> <ref bean="transactionManager" /> </property> <property name="target"> <ref bean="daoTarget" /> </property> <property name="transactionAttributes"> <props> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean>
The problem is that spring closes the hibernate session right after performing step 1. Therefore, I get a "LazyInit" exception when I want to add the child in step 3. I find this not logical, since spring is only allowed to close the session AFTER the transaction commit.
Agreed, the TX property of the DAO is set to REQUIRED. So the transaction commit will be right after the dao method executed, BUT ! Since there IS a running JTA transaction (the bean has a CMT) when I call the dao, I would think that the session must only be closed when the JTA transaction is commited, and that is NOT at the end of each dao call, but at the end of the bean method execution !
To test this, I created a class called "Delegate". I move all the code from the bean method into this class, and I execute this class from the bean method. Like:
[SSB method]
1. Delegate delegate = (Delegate) beanFactory.getBean("delegate");
2. delegate.process();
[Delegate.process() method]
1. DAO dao = (DAO) beanFactory.getBean("dao");
2. TransferObjectParent parent = dao.find(1);
3. TransferObjectChild child = new TransferObjectChild();
4. parent.addChild(child);
The application context would then be extended with this :
Now the lazy loading works, since the session remains open !Code:<bean name="delegateTarget" class="Delegate"/> <bean id="delegate" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager"> <ref bean="transactionManager" /> </property> <property name="target"> <ref bean="delegateTarget" /> </property> <property name="transactionAttributes"> <props> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean>
So it seems that spring considers the transaction end as the latest defined transactional object in its application context.
Now, what I want is that the session remains open until the container demarcated transaction is commit
Any one ideas ?
BTW:
1. I'm sure that there is a JTA transaction. If I set the TX attribute of the DAO to MANDATORY and I set the TX attribute for the sessionbean to NEVER, then I get an exception from spring complaining that there is no TX
2. I'm also sure that the DAO participates in the transaction. When I set the dao TX attribute to REQUIRES_NEW and I alter the code inside the session bean method:
1. DAO dao = (DAO) beanFactory.getBean("dao");
2. TransferObjectParent parent = new TransferObjectParent();
3. dao.insert(parent);
4. throw new RuntimeExceptio();
then the parent IS inserted in the database. When I do this with the TX attribute set to REQUIRED, the parent IS NOT inserted into the database. Which is like it should. When TX is requires new, it should insert ignoring the exceptions that may follow after the insert transaction. When the TX is required, the insert should be rolledback is exceptions occur after the insert.
Thanks for reading.


Reply With Quote