-->
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.  [ 2 posts ] 
Author Message
 Post subject: Moving Orphans (Products and Categories)
PostPosted: Tue Oct 05, 2004 4:28 pm 
Beginner
Beginner

Joined: Tue Mar 16, 2004 5:15 am
Posts: 33
Hibernate version: 2.x
Mapping documents:

Category->Product relation (parent->child)

<set name="products" lazy="true" inverse="true"
cascade="all-delete-orphan" sort="unsorted">
<key column="CATEGORY_FK"/>
<one-to-many class="xxx.Product"/>
</set>

Product->Category (child->parent)

<many-to-one name="category" class="xxx.Category"
cascade="none" outer-join="auto" update="true"
insert="true" column="CATEGORY_FK"
foreign-key="FK_PRODUCT_BELONGS_TO_CATEGORY"/>

Code between sessionFactory.openSession() and session.close():

Whats actually happens is:

//detach product from oldParent
oldParent.getProducts().remove(product);
product.setCategory(null);

//attach product to newParent category
newParent.getProducts().add(product);
product.setCategory(this);

[//flush it
flush()]

Summery and question:

Actually what I am trying to do is moving a product from one category to another. While moving a top-level product (parent-category=null) to
a category works well, moving a product to anywhere fails big time.

I guess the problem is that I have to remove the product from it's former category first and therfore the orphan is consider to get deleted too.

Hibernate seams to bind the orphan very stricktly to its parent following the composition assoziation with bound livecycle, once removed from parent it will stop existing.

So I am looking for a way to tell Hibernate that an adoptation is also occuring. You know one parent gives it child to another one and all three are living happily until the end of days.

Since this isn't covered in detail by the book (Hibernate in Action of cause) , I guess I have to ask the forum folks to help me.

Using anything other than the orphan cascade setting would force me to work through the entire object subgraph of the category and remove all children by myself if a delete occurs. Since the subgraph is quite huge this isn't a suitable option for me.

Another idea would be writing a SQL statement instead of using hibernate. Since a move action results in a rather simple update command (just category_fk=newValue) it is a no brainer. But I would like to keep things as abstract as possible, since this was what I thought I will get from Hibernate.

I have isolated the problem by a unit test (sure I did) and inlined the following code to proof the sql solution being a real solution for the problem:

Statement statement=getSession().connection().createStatement();
int affectedRowCount=statement.executeUpdate("update PRODUCTS set CATEGORY_FK="
+newParent.getId()+" where PRODUCT_ID="+product.getId()+" and "
+"CATEGORY_FK="+oldParent.getId());
assertEquals("One row should be affected by the statement",1,affectedRowCount);

I didn't used prepared statements for now, but I surely will if this enters production code. As long as it is on the unit test wildfarm it's ok. After calling the statement all affected elements have to be reloaded to be updated and before the statement, I flush the whole session. I believed I read a section about calling native SQL statments within the book but I only found the 'native SQL query' section... .

The sql solution seams to work and does only introduce minor problems. But one headache remains, it is not abstract nor simply to documentate, since it may raise many strange issues once the product is in production use.

So does anyone has a real 'Hibernate-solution' for my move orphans problem?

Full stack trace of any exception that occurs:

org.springframework.orm.hibernate.HibernateObjectRetrievalFailureException: deleted object would be re-saved by cascade (remove deleted object from associations): 13, of class: org.ocsn.domain.Category; nested exception is net.sf.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations): 13, of class: org.ocsn.domain.Category
at org.springframework.orm.hibernate.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:421)
at org.springframework.orm.hibernate.HibernateTransactionManager.convertHibernateAccessException(HibernateTransactionManager.java:480)
at org.springframework.orm.hibernate.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:398)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:376)
at org.springframework.transaction.interceptor.TransactionAspectSupport.doCommitTransactionAfterReturning(TransactionAspectSupport.java:278)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:67)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:140)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:153)
at $Proxy0.setParent(Unknown Source)
... 36 more
Caused by: net.sf.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations): 13, of class: org.ocsn.domain.Category
at net.sf.hibernate.impl.SessionImpl.forceFlush(SessionImpl.java:752)
at net.sf.hibernate.impl.SessionImpl.save(SessionImpl.java:730)
at net.sf.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:1376)
at net.sf.hibernate.engine.Cascades$4.cascade(Cascades.java:114)
at net.sf.hibernate.engine.Cascades.cascade(Cascades.java:436)
at net.sf.hibernate.engine.Cascades.cascadeCollection(Cascades.java:526)
at net.sf.hibernate.engine.Cascades.cascade(Cascades.java:452)
at net.sf.hibernate.engine.Cascades.cascade(Cascades.java:503)
at net.sf.hibernate.engine.Cascades.cascade(Cascades.java:482)
at net.sf.hibernate.impl.SessionImpl.preFlushEntities(SessionImpl.java:2673)
at net.sf.hibernate.impl.SessionImpl.flushEverything(SessionImpl.java:2250)
at net.sf.hibernate.impl.SessionImpl.flush(SessionImpl.java:2239)
at net.sf.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:61)
at org.springframework.orm.hibernate.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:386)

Name and version of the database you are using:

Mysql + HSQL-DB + Spring


Top
 Profile  
 
 Post subject:
PostPosted: Mon Nov 15, 2004 9:02 am 
Newbie

Joined: Fri Nov 12, 2004 5:52 am
Posts: 1
I had the same problem.

How I have solved this:

1. set cascade to "all" - this will leave orphan children: when children are removed from parent's set, children have null as their parent (in your case Product which was removed from set of some Category, then will have null for it's Category)

2. delete orphans manually (in general you will need to modify code which saves or deletes parent (Category in your case ) )

3. add code which will do this:
- detach Product from source Category
- save source Category (no need to save category because of cascade="all") - this will update reference between source Category and Product, but the product will remain.
- attach Product to destionation Category
- save destionation Category - this will update reference betwee destionation Category and Product, finnaly, the product will on its target place

Your code for point 3 might look like this:
===============================================
sourceCategory.getProducts().remove( product );
product.setCategory( null );
session.saveOrUpdateCopy( sourceCategory ); // leaves an orphan

destinationCategory.getProducts().add( product );
product.setCategory( destinationCategory );
session.saveOrUpdateCopy( destinationCategory );
===============================================

Tested on HSQL-1.7.2.4 + Hibernate-2.1.6 + Spring-1.1.1


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 2 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.