Struts2; keeping a session open for StrutsSpringTestCase JUnit tests
My project architecture is Struts2 with Spring integration and
JPA/Hibernate. StrutsSpringTestCase base class is utilized for JUnit
integration tests.
Under normal circumstances, the following configuration in web.xml keeps a
single session open from start to finish of each request:
<filter>
<filter-name>Spring OpenEntityManagerInViewFilter</filter-name>
<filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Spring OpenEntityManagerInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
As a result, all lazy loading works fine in all services. For example:
@Override
public Person find(int id) {
Person person = personDao.find(id);
// Take care of lazy loading before detaching the object for
// the view layer...
person.getGender().getCode();
// Detach the object so that it can be used for data transfer
// (as a DTO) without causing JPA issues and errors...
getEntityManager().detach(person);
return person;
}
Now... issues arise when I try to run the integration tests, which are
independent of the OpenEntityManagerInViewFilter configuration in web.xml.
What happens is that since there is no session being kept open from start
to finish of each request, lazy loading statements like
"person.getGender().getCode()" don't work any longer, and I get the "could
not initialize proxy - no Session" errors.
One solution I'm aware of is to force the @Transactional annotation upon
the service methods that are having the lazy-loading issues, which will
result in a session being open from start to finish of the method call. I
tested it and it fixed the problem:
@Transactional
@Override
public Person find(int id) {
Person person = personDao.find(id);
// Take care of lazy loading before detaching the object for
// the view layer...
person.getGender().getCode();
// Detach the object so that it can be used for data transfer
// (as a DTO) without causing JPA issues and errors...
getEntityManager().detach(person);
return person;
}
However, this could be overkill as the method doesn't need a transaction
under normal circumstances. I'm wondering if there is another solution
that doesn't require to compromise on the service side.
Is there something I can add to my test classes (which extend
StrutsSpringTestCase) to keep the session open? Or is there perhaps an
elegant configuration solution on the Spring or JUnit side?
Here is my Spring configuration file - applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"
default-dependency-check="all"
default-lazy-init="false"
default-autowire="byName">
<!-- *************** MAIN CONFIGURATION SECTION *************** -->
<!-- Bean post-processor for JPA annotations. -->
<!-- Make the Spring container act as a JPA container and inject an
EnitityManager from
the EntityManagerFactory. -->
<bean
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"
autowire="no"
dependency-check="none" />
<!-- ** Data Source Configuration ** -->
<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close"
autowire="no"
dependency-check="none">
<!-- Database configuration: -->
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://localhost/**********" />
<property name="user" value="**********" />
<property name="password" value="**********" />
<!-- C3P0 pooling properties configuration: -->
<property name="acquireIncrement" value="4" />
<property name="initialPoolSize" value="4" />
<property name="minPoolSize" value="4" />
<property name="maxPoolSize" value="20" />
<property name="maxIdleTime" value="600" />
<property name="maxConnectionAge" value="1800" />
</bean>
<!-- ** JPA Vendor Selection ** -->
<bean id="jpaVendorAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
autowire="no"
dependency-check="none" />
<!-- ** JPA Vendor and Entity Manager Configuration ** -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
autowire="no"
dependency-check="none">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
<property name="jpaProperties">
<props>
<prop
key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
<!-- Have the JPA vendor manage the database schema: -->
<prop key="hibernate.hbm2ddl.auto">create</prop>
<prop
key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.cache.use_query_cache">true</prop>
<prop
key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>
<prop key="hibernate.max_fetch_depth">4</prop>
<prop key="hibernate.jdbc.batch_size">1000</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.format_sql">false</prop>
</props>
</property>
</bean>
<!-- ** Transaction Manager Configuration ** -->
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager"
autowire="no"
dependency-check="none">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!-- ** Transaction Annotation Configuration; classes/functions with
@Transactional will
get a framework transaction. ** -->
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- **** DETAILED SERVICE BEAN CONFIGURATION WAS TAKEN OUT TO SHORTEN
THE FILE **** -->
</beans>
I would appreciate any pointers.
No comments:
Post a Comment