views:

346

answers:

2

I'm having a problem where the addition of spring's transaction management to an application causes Hibernate to throw the following error:

org.hibernate.HibernateException: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: org.fstrf.masterpk.domain.ReportCriteriaBean.treatmentArms
org.hibernate.engine.Collections.processDereferencedCollection(Collections.java:96)
org.hibernate.engine.Collections.processUnreachableCollection(Collections.java:39)
org.hibernate.event.def.AbstractFlushingEventListener.flushCollections(AbstractFlushingEventListener.java:218)
org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:77)
org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:26)
org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
org.springframework.orm.hibernate3.SpringSessionSynchronization.beforeCommit(SpringSessionSynchronization.java:135)
org.springframework.transaction.support.TransactionSynchronizationUtils.triggerBeforeCommit(TransactionSynchronizationUtils.java:72)
org.springframework.transaction.support.AbstractPlatformTransactionManager.triggerBeforeCommit(AbstractPlatformTransactionManager.java:905)
org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:715)
org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:701)
org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:321)
org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:116)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
$Proxy92.saveNewReportCriteria(Unknown Source)
org.fstrf.masterpk.domain.logic.MasterPkFacade.saveNewReportCriteria(MasterPkFacade.java:134)
org.fstrf.masterpk.controllers.ReportCriteriaController.setupReportType(ReportCriteriaController.java:302)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
java.lang.reflect.Method.invoke(Method.java:585)
org.springframework.web.bind.annotation.support.HandlerMethodInvoker.doInvokeMethod(HandlerMethodInvoker.java:413)
org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:134)
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:310)
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:297)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:875)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:809)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:571)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:511)
javax.servlet.http.HttpServlet.service(HttpServlet.java:710)
javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)

I'm using Spring 2.5 and annotations to implement this management. Here is the class containing the saveNewReportCriteria method (which, as can be seen by the stack trace, is causing the error)

@Transactional(
     propagation = Propagation.REQUIRED,
     isolation = Isolation.DEFAULT,
     readOnly = false)
public class HibernateReportCriteriaDao implements ReportCriteriaDao{

private HibernateTemplate hibernateTemplate;

public Integer saveNewReportCriteria(ReportCriteriaBean reportCriteria) {
    hibernateTemplate.save(reportCriteria);

    List<Integer> maxIdList = hibernateTemplate.find("SELECT max(id) from ReportCriteriaBean");
        logger.info("ID of newly saved list is: " + maxIdList.get(0));
        return maxIdList.get(0);
    }

    public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
        this.hibernateTemplate = hibernateTemplate;
    }
}

Then I added the following sections to my configuration files to tell spring that I am using annotation driven transaction management:

<bean id="actgDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="jdbc/actg" />
    <property name="resourceRef" value="true" />
</bean>

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="actgDataSource" />
</bean>

<tx:annotation-driven/>

I'm pretty sure that the de-referencing error is being caused due to the proxy class that Spring AOP creates and uses in order to handle transaction management, but I have no idea how I'd go about fixing it.

+1  A: 

the error might well NOT be in that function, if you look carefully at the stack trace you'll see that the error is being called from the PROXY's saveNewReportCriteria and not your class's. this is likely something earlier that did an update or delete and wasn't flushed immediately to the db by hibernate because it can optimise certain things that way.

one way to find out what's happening is to put a .flush() at the end of every delete/update query, which isn't always desirable, or alternatively just do a search for where you might be setting treatmentArms to null or a new List() - to clear a list on hibernate use list.clear(); which will keep the hibernate-proxied collection the correct type.

oedo
I did see that the error was coming from the proxy, but I think the de-referencing error is caused from the CREATION of that proxy. I had already modified the setter for the treatmentArms collection of my bean so that it should not be set to null or a new List. My guess is that this null/new assignment is happening during the creation of the proxy class, I'm not sure how to avoid this though. I'll try adding the 'flush' statement to my DAO methods.
TimmyJ
just to clarify, there are 2 proxies at work here - the proxy that spring builds around your `HibernateReportCriteriaDao` class to provide transactional support, and the proxy/s that hibernate uses within/around your `ReportCriteriaBean` class. good luck with the flush() method, it should tell you where the problem is anyway.
oedo
Adding the flush() method didn't seem to do anything for me. I forgot to mention earlier that even though this exception is thrown, all the data is persisted to the DB, which seems wrong to me.Also, if it's isn't clear from my post. This same code worked fine before the addition of the transaction management (and thus, before the addition of the HibernateReportCriteriaDao proxy)
TimmyJ
that is most confusing then. are you sure you're not called setTreatmentArms with a new list somewhere? to replace all values you'll need to .clear() and then .addAll() to the existing list. my practice is to initialise the list in the variable list (eg private List<x> myList = new LinkedList<x>) and not have a constructor, and then not ever use the hibernate-required setMyList(..) function in my code...
oedo
Thanks for your help with this oedo. I've "solved" the problem and would love to hear if you have any hypotheses as to why this is causing a problem.
TimmyJ
how did you solve it then?
oedo
Sorry, I should have mentioned that I answered my own question below (although apparently I'm not allowed to accept that answer for a couple days).
TimmyJ
A: 

Apparently my problem did lie with the saveNewReportCriteria method. Removal of the second query (the find() call for the maximum ID) caused the error to go away. I have no idea why this is the case. As I mentioned earlier, this method was fine prior to the addition of the transaction management annotations. If anyone knows what is going on here I'd love to find out.

TimmyJ
cool, i hope this stays working for you, but i strongly suspect that the problem might bite you again - the call to find() and the addition of transactions should not cause the error you are getting. good luck anyway :)
oedo