Sunday, April 19, 2009

Testing EJB out of application server - Part 3. Spring managed DAO tier

After a short delay and thinking about giving Spring to manage stateless DAO tier beans, here comes 3-rd part of the article. There were some issues that had to be investigated to make the topic complete. Most of all, there was an open question of controlling Hibernate Session
life cycle, to have OpenSessionInView pattern working.
Let's define what we want to gain with DAO bean management by Spring:
1. Keep DAO beans working with EntityManager to make CRUD operations with entities.
2. Make switch from EJB' @Stateless (and even @Stateful) annotations to Spring' @Service (or have beans configured in XML file).
3. Have bean auto wiring in DAO with Spring IoC, avoiding JNDI lookups and extra configuration in XML file.
4. Ensure transaction and session management is compliant with EJB approach to handle entity beans lazy initialization problem (here I refer to interceptor and callback/template approaches).

Let's examine what changes are to be done to our simple Internet shop application.

DAO tier


Since we do not need to manage DAO beans with EJB but Spring, just change annotations on DAO bean class level:

@Service("purchaseDAO")
public class PurchaseDAOImpl extends AbstractDAO<Purchase> implements PurchaseDAO {
public PurchaseDAOImpl() {
super(Purchase.class);
}
}


Here, we used @Service that is recognized by Spring component scanning functionality we already familiar with. Also, DAO interfaces do not need @Local nor @Remote annotation (so they are removed):

public interface PurchaseDAO extends DAO<Purchase> {
}


There is also a simplification in application context configuration file - DAO beans refering through JNDI to stateful beans managed by EJB have been removed.
Important configuration step is adding <context:annotation-config/> to application context configuration file. This will include support for JPA annotations like @PersistenceContext. To have everything working we need to define bean, with name of persistence unit, which refers to EntityManagerFactory:

<bean id="entityManagerFactory"
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject"
ref="entityManagerBean"/>
<property name="targetMethod"
value="getEntityManagerFactory"/>
</bean>

<alias alias="ishopPU"
name="entityManagerFactory"/>


OpenSessionInView pattern


To be correct, in Spring terms pattern is named OpenEntityManagerInView. There is a significant difference between this new approach and old-style one used by applications, built upon Hibernate ORM, to keep Hibernate Session open for a request processing cycle.
Opening Hibernate Session in filter and binding it to SessionFactory these applications relied on the fact entity beans extracted in DAO tier are kept attached to this session until request has been processed and session is closed, so access to lazily initialized references would not cause an exception.
But the approach, in it's original implementation, does not work for EJB - each transactional method is completed within its own EntityManager instance which holds reference to it's distinct Hibernate Session; opening new session does not make any affect.
Instead, using TransactionSynchronizationManager, we bind EntityManager to EntityManagerFactory so each transactional method executed with the same EntityManager, and as result the same Session, instance used:

@Stateful(name = "openSessionInViewCallback")
@LocalBinding(jndiBinding = "openSessionInViewCallbackLocal")
public class OpenSessionInViewCallbackImpl implements OpenSessionInViewCallback {
@PersistenceContext(type = PersistenceContextType.EXTENDED)
private EntityManager entityManager;

@PersistenceUnit(unitName = "ishopPU")
private EntityManagerFactory entityManagerFactory;

@Init
public void invoke(Runnable r) {
TransactionSynchronizationManager.bindResource(entityManagerFactory, new EntityManagerHolder(entityManager));

r.run();
}

@Remove
public void remove() {
TransactionSynchronizationManager.unbindResource(entityManagerFactory);
EntityManagerFactoryUtils.closeEntityManager(entityManager);
}
}


In @Init method we bind entity manager, and unbind it on callback method completion with @Remove cleanup. For more information on Spring implementation of the pattern please refer to org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter and org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor

Source code


Can be downloaded here.

That's it!

No comments:

Post a Comment