-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 9 posts ] 
Author Message
 Post subject: EntityManager not deleting from many-to-X
PostPosted: Sun Aug 17, 2008 4:41 am 
Newbie

Joined: Thu Dec 09, 2004 4:26 am
Posts: 19
Location: Yorkshire, UK
Hi,

I have a Will entity that has

many-to-many Solicitors
many-to-one Executors

When I add to the collections in the Will, the new collections are persisted perfectly to the database.

e.g.

Code:
// will, solicitor and executor are all managed
will.getSolicitors().add(solicitor);
will.getExecutors().add(executor);
getEntityManager().merge(will);


...but when I remove from the collections, and merge, no update statements go to the database. The current instance of the Will is OK but when reloaded/refreshed from the database I can see that the removed solicitors and executors are loaded back into the collections.

e.g.
Code:
// will, solicitor and executor are all managed
will.getSolicitors().remove(solicitor);
will.getExecutors().remove(executor);
getEntityManager().merge(will);


I've mapped the executors and solicitors like this...

Will model
Code:
    @OneToMany(fetch = FetchType.EAGER, mappedBy = "will", cascade = { CascadeType.ALL })
    @Cascade( { org.hibernate.annotations.CascadeType.MERGE })
    private Set<Executor> executors;

    @ManyToMany(fetch = FetchType.EAGER, mappedBy = "wills", cascade = { CascadeType.ALL })
    @Cascade( { org.hibernate.annotations.CascadeType.SAVE_UPDATE })
    private List<Solicitor> solicitors;


Note I've tried Set,List and numerous variations on Cascade to see if that makes any difference.

Executor
Code:
    @ManyToOne(optional = true, cascade = { CascadeType.ALL })
    @Cascade( { org.hibernate.annotations.CascadeType.MERGE })
    private Will will;


Solicitor
Code:
    @ManyToMany(fetch=FetchType.EAGER, cascade = { CascadeType.ALL })
    private List<Will> wills;


Thanks for any help.

Tim

Hibernate version: 3.2
Spring 2.0
PostgreSQL 8.3
Code:
   <bean id="dataSource"
      class="com.mchange.v2.c3p0.ComboPooledDataSource"
      destroy-method="close">
      <property name="driverClass" value="${jdbc.driver}" />
      <property name="jdbcUrl" value="${jdbc.url}" />
      <property name="user" value="${jdbc.username}" />
      <property name="password" value="${jdbc.password}" />
   </bean>

   <bean id="entityManagerFactory"
      class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
      <property name="dataSource" ref="dataSource" />
      <property name="jpaVendorAdapter">
         <bean
            class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
            <property name="showSql" value="${jdbc.show_sql}" />
            <property name="generateDdl" value="${update.schema}" />
            <property name="databasePlatform"
               value="${jdbc.dialect}" />
         </bean>
      </property>
   </bean>

   <bean id="transactionManager"
      class="org.springframework.orm.jpa.JpaTransactionManager">
      <property name="entityManagerFactory"
         ref="entityManagerFactory" />
      <property name="dataSource" ref="dataSource" />
   </bean>

   <tx:annotation-driven />


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 19, 2008 1:35 pm 
Hibernate Team
Hibernate Team

Joined: Thu Apr 05, 2007 5:52 am
Posts: 1689
Location: Sweden
Hi,

what's about equals() and hashCode() - are you overriding these methods in Solicitors and Executors?

--Hardy


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 19, 2008 3:53 pm 
Newbie

Joined: Thu Dec 09, 2004 4:26 am
Posts: 19
Location: Yorkshire, UK
Hi Hardy, Thanks for your suggestion.

Unfortunately Solicitor and Executor models do have equals and hashCode methods. Can you see any problems? I've been copy and pasting the same methods for years!

Code:
    @Override
    public boolean equals(Object that) {
        if (!(that instanceof Solicitor))
            return false;
        else if (getId() == null)
            return false;
        else
            return getId().equals(((Solicitor) that).getId());
    }
   
    @Override
    public int hashCode(){
        if ( getId() == null )
            return super.hashCode();
        else
            return getId().hashCode()+929;
    }


Thanks again, Tim


Top
 Profile  
 
 Post subject:
PostPosted: Thu Aug 21, 2008 8:07 am 
Hibernate Team
Hibernate Team

Joined: Thu Apr 05, 2007 5:52 am
Posts: 1689
Location: Sweden
Hi,

normally it is recommended that you implement equality with a business key - meaning a set of properties that is unique for the each instance with the same db identifier. Since you are saying, however, that all your entities in your example are already managed it should not matter in this case.

How does your code continue after the merge? Do you keep working with the instance you passed to merge()? That would be wrong. You always have to use the entity returned from merge.

Have you turned on the SQL debug log? Are you sure that the session get flushed() or closed()?

--Hardy


Top
 Profile  
 
 Post subject:
PostPosted: Sun Aug 24, 2008 5:43 pm 
Newbie

Joined: Thu Dec 09, 2004 4:26 am
Posts: 19
Location: Yorkshire, UK
Hi Hardy,

Sorry for the delay, been side-tracked by another project.


Yep, there is a flush() right after merge. I then continue using the newly merged version...

Code:
    public Will save(Will will) {
        Will saved = getEntityManager().merge(will);
        getEntityManager().flush();
        return saved;
    }


I did start with a business key but it is possible for separate users to enter identical solicitor, executor or will details so we'd rather have duplicates than users able to edit detail of the others executors.

This is really driving me crazy. This code is based on other projects that I've written over the last 4 or 5 years of using Spring/Hibernate and lately JPA and annotations. I must have gone over the code 10's of times and compared it to other projects. It may be time to re-write from scratch, probably quicker than going round in circles!

The site is live at the moment, even with the rough edges. Customers can be so pushy!

Thanks again,
Tim
http://www.tnwdb.com


Top
 Profile  
 
 Post subject:
PostPosted: Mon Aug 25, 2008 3:18 am 
Hibernate Team
Hibernate Team

Joined: Thu Apr 05, 2007 5:52 am
Posts: 1689
Location: Sweden
Hi,

have you tried upgrading to the latest Hibernate version? Hibernate Core 3.3.0.SP1 and Annotations 3.4.0.GA?


Top
 Profile  
 
 Post subject:
PostPosted: Mon Aug 25, 2008 6:06 am 
Newbie

Joined: Thu Dec 09, 2004 4:26 am
Posts: 19
Location: Yorkshire, UK
Hi again,

I've now updated to Hibernate 3.3.0SP1, Annotations 3.4.0GA, EntityManager 3.4.0GA, c3p0 0.9.1 and Spring 2.5.5 but there's no change.

BTW, the list of jar's is much smaller now I'm not relying on MyEclipse for the libraries!

I was thinking of trying a different datasource pool, other than c3p0. Which would you recommend or stick with trusty c3p0?

A snip of my applicationContext.xml is below. I know I've taken a lot of your time but if you could have a look and see if there are problems or recommended optimisations, I'd be grateful.

Thanks.

Code:
...
   <bean id="dataSource"
      class="com.mchange.v2.c3p0.ComboPooledDataSource"
      destroy-method="close">
      <property name="driverClass" value="${jdbc.driver}" />
      <property name="jdbcUrl" value="${jdbc.url}" />
      <property name="user" value="${jdbc.username}" />
      <property name="password" value="${jdbc.password}" />
   </bean>

   <bean id="entityManagerFactory"
      class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
      <property name="dataSource" ref="dataSource" />
      <property name="jpaVendorAdapter">
         <bean
            class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
            <property name="showSql" value="${jdbc.show_sql}" />
            <property name="generateDdl" value="${update.schema}" />
            <property name="databasePlatform"
               value="${jdbc.dialect}" />
         </bean>
      </property>
   </bean>

   <bean id="transactionManager"
      class="org.springframework.orm.jpa.JpaTransactionManager">
      <property name="entityManagerFactory"
         ref="entityManagerFactory" />
      <property name="dataSource" ref="dataSource" />
   </bean>

   <tx:annotation-driven />

   <bean
      class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
   <bean
      class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />

   <bean
      class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">
      <property name="transactionInterceptor"
         ref="transactionInterceptor" />
   </bean>

   <bean id="transactionInterceptor"
      class="org.springframework.transaction.interceptor.TransactionInterceptor">
      <property name="transactionManager" ref="transactionManager" />
      <property name="transactionAttributeSource">
         <bean
            class="org.springframework.transaction.annotation.AnnotationTransactionAttributeSource" />
      </property>
   </bean>
   <!-- End transaction management -->
...


Top
 Profile  
 
 Post subject:
PostPosted: Mon Aug 25, 2008 6:07 am 
Newbie

Joined: Thu Dec 09, 2004 4:26 am
Posts: 19
Location: Yorkshire, UK
The Will instance returned by the merge() does contain the correct values. i.e. the collections have less solicitors or executors.

If I do ...

Code:
assertTrue(will.getSolicitors().size()==2);
will.getSolicitors().remove(will.getSolicitors().get(0)); //remove a solicitor from the test will
Will mergedWill = getEntityManager().merge(will); //persist
assertTrue(mergedWill.getSolicitors().size()==1); // check that one has been removed
will = getEntityManager().find(Will.class, mergedWill.getId()); // reload
assertTrue(will.getSolicitors().size()==1); //check that the collection has been persisted


...it returns an instance with the collections repopulated with the old values as if nothing was removed and the unit test fails on the last line.

Other basic fields, such as name, creation_date, etc are saved back to the database correctly. Just the many-to-X that are not persisted.

It's a national holiday here, better get back to the family!


Top
 Profile  
 
 Post subject:
PostPosted: Mon Aug 25, 2008 2:10 pm 
Newbie

Joined: Thu Dec 09, 2004 4:26 am
Posts: 19
Location: Yorkshire, UK
One last thing....


Reading through the debug, I can see that Hibernate finds two dirty collections:

Quote:
DEBUG - Collections - Collection found: [crystalmark.willfinder.model.Will.executors#113], was: [crystalmark.willfinder.model.Will.executors#113] (initialized)
DEBUG - Collections - Collection found: [crystalmark.willfinder.model.Will.solicitors#113], was: [crystalmark.willfinder.model.Will.solicitors#113] (initialized)
DEBUG - tractFlushingEventListener - Flushed: 0 insertions, 0 updates, 0 deletions to 329 objects
DEBUG - tractFlushingEventListener - Flushed: 0 (re)creations, 2 updates, 0 removals to 100 collections


2 Updates Great! but it then goes on to do

Quote:
DEBUG - JpaTransactionManager - Triggering beforeCommit synchronization
DEBUG - JpaTransactionManager - Triggering beforeCompletion synchronization
DEBUG - JpaTransactionManager - Initiating transaction commit
DEBUG - JpaTransactionManager - Committing JPA transaction on EntityManager [org.hibernate.ejb.EntityManagerImpl@79cfb151]
DEBUG - JDBCTransaction - commit
DEBUG - tractFlushingEventListener - processing flush-time cascades
DEBUG - tractFlushingEventListener - dirty checking collections
...
...
DEBUG - Collections - Collection found: [crystalmark.willfinder.model.Will.executors#113], was: [crystalmark.willfinder.model.Will.executors#113] (initialized)
DEBUG - Collections - Collection found: [crystalmark.willfinder.model.Will.solicitors#113], was: [crystalmark.willfinder.model.Will.solicitors#113] (initialized)
DEBUG - tractFlushingEventListener - Flushed: 0 insertions, 0 updates, 0 deletions to 329 objects
DEBUG - tractFlushingEventListener - Flushed: 0 (re)creations, 0 updates, 0 removals to 100 collections


The updates have been forgotten. Does this help diagnose?

Thanks again. Tim


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 9 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.