I am fairly new to both Hibernate3 and Spring3, and I'm having an issue related to initializing lazy references of a hibernate object.
I have a fully contained dao and service. The domain objects are created using hbm2java and a reverse engineering file. I have followed some best practices I have found using Annotations (@Transactional) on my service objects. (This guide was VERY helpful to me http://carinae.net/2009/11/layered-architecture-with-hibernate-and-spring-3/)
The issue that I am having is that I have the following spring configuration in my service.jar for annotation processing and transaction management:
<context:annotation-config />
<context:component-scan base-package="com.barlingbay.dodmerb.persistence" />
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
</bean>
<tx:annotation-driven/>
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
My hibernate.cfg.xml is simply a listing of my datasource details and the annotation mappings of the domain objects.
I am using a service layer and a dao layer.
@Service
public class ApplicantEventServiceImpl implements ApplicantEventService {
@Autowired(required = true)
private ApplicantEventDao appEventDao;
@Transactional
public List<ApplicantEvent> getEvents() {
return this.appEventDao.getPendingEvents();
}
and a dao layer
@Repository
public class ApplicantEventDaoImpl implements ApplicantEventDao {
@Autowired(required = true)
private SessionFactory sessionFactory;
public List<ApplicantEvent> getPendingEvents() {
sessionFactory.getCurrentSession().beginTransaction(); // (If I don't have this, my junit test fails because of no active transaction, but that's a different issue)
return sessionFactory.getCurrentSession()
.createQuery("from ApplicantEvent").list();
}
This collection of code is packaged as a maven module and is included in as a dependency in another spring project which is a scheduling and workflow module that I am developing. Relevant applicationContext.xml info
<bean id="workflowStepper" class="com.barlingbay.merb.scheduler.WorkflowStepper" />
<bean id="jobDetailWorkflowStepper"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"
p:targetObject-ref="workflowStepper" p:targetMethod="execute" />
<bean id="triggerJobDetailTicketRegistryCleaner" class="org.springframework.scheduling.quartz.SimpleTriggerBean"
p:jobDetail-ref="jobDetailWorkflowStepper" p:startDelay="2000"
p:repeatInterval="5000" />
<bean id="scheduler"
class="com.barlingbay.merb.scheduler.AutowiringSchedulerFactoryBean" />
And a basic Workflow.java:
public class WorkflowStepper implements IWorkflowStepper,
ApplicationContextAware {
private final Logger LOG = Logger.getLogger(this.getClass());
private ApplicationContext applicationContext;
// @Transactional
public void execute() {
ApplicantEventService appEvent = (ApplicantEventService) applicationContext
.getBean("applicantEventServiceImpl");
List<ApplicantEvent> events = appEvent.getEvents();
for (ApplicantEvent event : events) {
try {
LOG.info(event.getApplicant().getUsername() + "[" + event.getName()
+ "]");
....
I get (and I understand why I get) a LazyInitializationException during the LOG.info statement. The transaction is managed by the spring-hibernate context in the included service maven dependency and is not available in this context. What I don't understand is the proper way to include the transaction management into this layer (and beyond) to prevent the LazyInitializationException. I tried simply adding
<bean id="txInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor" />
to the business layer's application context, which it complained that it couldn't see the transactionmanager defined in the service dependency. However, if I do add @Transactional to the .execute() of the Workflow stepper, it does "inherit" the configuration from the dependency.
What am I missing?
You can fight through Lazy-initialization using an OpenEntityManagerInView interceptor in your application. Else you can call some getter method on one of the ApplicationEvent in the list of events inside the getPendingEvents() method. This will eagerly load the objects inside the lists.
EDIT: You dont seem to use JPA in your application so OpenEntityManagerInView might not be an object. The second option should still work.