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.  [ 11 posts ] 
Author Message
 Post subject: Cascading delete question - short version
PostPosted: Wed Mar 22, 2006 9:03 pm 
Beginner
Beginner

Joined: Fri Feb 24, 2006 1:18 pm
Posts: 25
Hey folks - I don't know if I the following is a feature of Hibernate (2.1.8) or a problem with my code/configuration.

The core of the issue are three objects - Camp, CampWeek, ChildrenInCampWeek (yes, a little Camp Reservation system for the kids). A Camp has many CampWeeks and each CampWeek can have many children in it.

When I delete the Camp, I am expecting that all the CampWeeks for that Camp should be deleted and that any ChildInCampWeeks for those CampWeeks should also be deleted.

When I have one child in a number of CampWeeks for Camp1, everything works as outlined above. However, when I have a second child in those same CampWeeks, then I get errors such as:
DELETE statement conflicted with COLUMN REFERENCE constraint 'FK_ChildInCampWeek_CampWeek'. The conflict occurred in database 'Reservations', table 'ChildInCampWeek', column 'CampWeekId'.

Without getting too lengthy here, the Camp.hbm contains:
Code:
<set cascade="all-delete-orphan" inverse="true" name="CampWeekSet" sort="camp.CampWeek$CampWeekComparator">
   <key column="CampId" />
   <one-to-many class="CampWeek" />
</set>

CampWeek.hbm has:
Code:
<set
   cascade="all-delete-orphan"
   name="ChildInCampWeekSet"
   table="ChildInCampWeek"
   sort="camp.ChildInCampWeek$ChildInCampWeekComparator"
   inverse="true"         
>
   <key column="CampWeekId" />
   <one-to-many class="ChildInCampWeek" />
</set>

ChildInCampWeek.hbm has:
Code:
<many-to-one
   class="Child"
   name="Child"
   not-null="true"
>   
  <column name="ChildId" />
</many-to-one>
<many-to-one
   class="CampWeek"
   name="CampWeek"
   not-null="true"
>
  <column name="CampWeekId" />
</many-to-one>

my code is simply:
Code:
  public static void deleteCamp(Camp camp) throws HibernateException {
    Session session = getSession();
    Transaction tx = null;
    try {     
      tx = session.beginTransaction();
      session.delete(camp);
      tx.commit();
    } catch (HibernateException e) {
      System.out.println("Rolling back");
      if (tx != null) {
        tx.rollback();
      }
      e.printStackTrace();
    } finally {
      session.close();
    }
  }


Am I doing something wrong here? Thanks so much!!

PS - By mistake I posted this in the wrong forum originally - my apologies!


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 22, 2006 11:05 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
In the two set keys, add not-null="true". This ensures that hibernate won't update CampWeeks to have no Camps, or Childs to have no CampWeeks. All it changes in SQL is the order of updates.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 23, 2006 12:46 am 
Beginner
Beginner

Joined: Fri Feb 24, 2006 1:18 pm
Posts: 25
tenwit:

Thank you so much for the quick reply and I hate to have to start out by being an idiot - but how exactly do I add the not-null="true" to my set keys? I tried:
Code:
<set
   cascade="all-delete-orphan"
   name="ChildInCampWeekSet"
   table="ChildInCampWeek"
   sort="camp.ChildInCampWeek$ChildInCampWeekComparator"
   inverse="true"
>
   <key column="ChildId" not-null="true" />
   <one-to-many class="ChildInCampWeek" />
</set>


But I get errors of the form:
19:49:49,040 INFO [Configuration] Mapping resource: camp/Child.hbm
19:49:49,102 ERROR [XMLHelper] Error parsing XML: XML InputStream(36) Attribute "not-null" must be declared for element type "key".
net.sf.hibernate.MappingException: Error reading resource: camp/Child.hbm

I checked and not-null is already declared in the ChildInCampWeek.hbm.
Note: I am on Hibernate 2.1.8, not 3.x (if that matters). The example in the Hibernate3 documentation appears to match what I have done and I haven't quite found the 2.x version.

I am hoping/praying the fix is as simple as you have written!
RB


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 23, 2006 1:06 am 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
Hmm, not-null isn't a valid attribute for key in Hibernate 2. Knuts.

The nasty solution, and the only one I can think of that will work (until you upgrade), is to remove the not-null database constraint on the two columns that are foreign keys to "parent" tables. That would be CampWeekId in Child, and (I'm guessing) CampId in CampWeek. If you're generating the schema using an hibernate tool, then you're SOL, I'm afraid.

Hibernate will correctly delete the orphaned rows after updaing the foreign key to null. It's inefficient (updating a row you know you're about to delete), but it will work. And when you upgrade, you can can add not-null="true" to the key elements, and re-instate the not-null constraint in the DB.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 23, 2006 1:28 am 
Beginner
Beginner

Joined: Fri Feb 24, 2006 1:18 pm
Posts: 25
Again, many thanks for your quick responses, but my story gets sadder...
1. I removed all "not null" constraints from my database tables, leaving only those for primary keys. Re-ran my Junit tests - same results.
2. In the error, I noticed a reference to foreign key constraints, so I went into SQL Server and removed the foreign key constraints that were once generated for this database (all DB manipulation is done through Hibernate, so that doesn't trouble me).

Now, when I run the tests, I get the problem below - "No Row with the given identifier exists" (probably because it now been deleted).

One general debugging question - is there any way to see the values that are actually being used in the queries (when show_sql is true) as the "?"s aren't that useful to me.

We are probably diving a bit far down into the complexities of my application - but I thank you for your help. Unfortunately, upgrading to Hibernate v3 is not yet an option.

Thanks!
RB

Here is the new error...


bernate: select campweek0_.CampWeekId as CampWeekId2_, campweek0_.Description as Descript2_2_, campweek0_.WeekId as WeekId2_, campweek0_.CampId as CampId2_, week1_.WeekId as WeekId0_, week1_.StartDate as StartDate0_, camp2_.CampId as CampId1_, camp2_.Website as Website1_, camp2_.City as City1_, camp2_.StreetAddr as StreetAddr1_, camp2_.Name as Name1_, camp2_.Phone as Phone1_, camp2_.Cost as Cost1_, camp2_.Hours as Hours1_, camp2_.AfterCare as AfterCare1_, camp2_.Notes as Notes1_ from CampWeek campweek0_ left outer join Week week1_ on campweek0_.WeekId=week1_.WeekId left outer join Camp camp2_ on campweek0_.CampId=camp2_.CampId where campweek0_.CampWeekId=?
HibernateException in loadParent
21:21:58,533 INFO [PropertyMessageResources] Initializing, config='org.apache.struts.action.ActionResources', returnNull=true
net.sf.hibernate.UnresolvableObjectException: No row with the given identifier exists: 77, of class: camp.CampWeek
at net.sf.hibernate.UnresolvableObjectException.throwIfNull(UnresolvableObjectException.java:38)
at net.sf.hibernate.impl.SessionImpl.internalLoad(SessionImpl.java:1964)
at net.sf.hibernate.type.ManyToOneType.resolveIdentifier(ManyToOneType.java:69)
at net.sf.hibernate.type.EntityType.resolveIdentifier(EntityType.java:208)
at net.sf.hibernate.impl.SessionImpl.initializeEntity(SessionImpl.java:2219)
at net.sf.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:319)
at net.sf.hibernate.loader.Loader.doQuery(Loader.java:309)
at net.sf.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:138)
at net.sf.hibernate.loader.Loader.loadCollection(Loader.java:1020)
at net.sf.hibernate.loader.Loader.loadCollection(Loader.java:995)
at net.sf.hibernate.loader.OneToManyLoader.initialize(OneToManyLoader.java:93)
at net.sf.hibernate.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:288)
at net.sf.hibernate.impl.SessionImpl.initializeCollection(SessionImpl.java:3315)
at net.sf.hibernate.collection.PersistentCollection.forceInitialization(PersistentCollection.java:336)
at net.sf.hibernate.impl.SessionImpl.initializeNonLazyCollections(SessionImpl.java:3168)
at net.sf.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:143)
at net.sf.hibernate.loader.Loader.doList(Loader.java:1063)
at net.sf.hibernate.loader.Loader.list(Loader.java:1054)
at net.sf.hibernate.hql.QueryTranslator.list(QueryTranslator.java:854)
at net.sf.hibernate.impl.SessionImpl.find(SessionImpl.java:1554)
at net.sf.hibernate.impl.QueryImpl.list(QueryImpl.java:49)
at net.sf.hibernate.impl.AbstractQueryImpl.uniqueResult(AbstractQueryImpl.java:550)
at camp.DBLayer.loadParent(DBLayer.java:147)


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 23, 2006 1:37 am 
Beginner
Beginner

Joined: Fri Feb 24, 2006 1:18 pm
Posts: 25
Some additional information, staying at the more conceptual level. The test basically involves assigning 10 weeks of Camp1 to child1, 10 weeks of Camp2 to chilcd2, 10 weeks of Camp1 to Child3 (so Camp1 has both Child1 and Child3). Then I delete Camp1, thinking it will delete the 10 CampWeeks associated with Camp1 and the 20 ChildInCampWeeks that link child1 and child3 to Camp1.

Before, the delete would not occur. Now, with all the constraints removed, what happens (from looking at the database). Is that Camp1 is removed and the 10 CampWeeks associated with it. Also, the 10 ChildCampWeeks for Child1 are removed, but NOT the 10 ChildCampWeeks for Child3. Thus, when the system tries to load the Parent, it loads all the children and Child3 has a bunch of invalid references.

So, the conceptual question is - shouldn't deleting a Camp delete all its CampWeeks and all the ChildInCampWeeks as well? Or, am I asking too much of the cascade operation?

Thanks again - as this puzzle is driving me a bit crazy :).

RB


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 23, 2006 5:24 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
To view the parameters bound to and retrieved from hiberante-generated SQL queries, add this line to your log4j.properties:
Code:
log4j.logger.net.sf.hibernate.type=DEBUG
For even more info on the inner workings, add this line too:
Code:
log4j.logger.net.sf.hibernate.persister=DEBUG


For your main problem, it looks like you may be stuck. This is presumably exactly why not-null="true" was added to <key> elements in HHH. At this point I'd have to say that your options are to do the cascading yourself (recurse through the hierarchy deleting everything), or make all cascading-delete relationships unidirectional, so that the order of parent/child deletes is unimportant. Or, of course, upgrade.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 23, 2006 5:35 pm 
Beginner
Beginner

Joined: Fri Feb 24, 2006 1:18 pm
Posts: 25
Thanks for the logging information - that will be very helpful. I have found another rather strange anomaly - it seems that when I try to add a new ChildInCampWeek to the set that is stored in a CampWeek, it isn't being added to the set. That is to say, the set of ChildInCampWeeks associated with a particular CampWeek (the number of children attending that camp) never increases above 1.

Even the most basic test...
System.out.println("(Before) Size of this CW's CiCW set is: " + campWeek.getChildInCampWeekSet().size());
campWeek.addToChildInCampWeekSet(childInCampWeek);
System.out.println("(After) Size of this CW's CiCW set is: " + campWeek.getChildInCampWeekSet().size());

shows no increase in size. I would think that this would mean that I am trying to add an element that is already there, but the ids are different.

So, I am figuring that there is some more fundamental problem to be solved and when I do that, I will roll back up to the cascading problem.

Thanks again (and if you have any advice on this more mysterious problem, please send it along).....
RB


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 23, 2006 7:19 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
What's in your addToChildInCampWeekSet() method? It may be that you're falling victim to field/property level proxying confusion. Consider this code in class ChildInCampWeek:
Code:
private Set<Child> m_setChildren = new HashSet<Child>();

public Set<Child> getChildren() { return m_setChildren; }

public void setChildren(Set<Child> children) { m_setChildren = children; }

public void addToChildInCampWeekSet(Child child) { m_setChildren.add(child); }

void testMethod()
{
  CampWeek cw = getSession().get(CampWeek.class, TEST_ID);
  Child ch = ChildFactory.newChild("Alfred");
  cw.addToChildInCampWeekSet(ch);
  getSession().save(cw);
}
If you have used the default access method (property), then this code is buggy. m_setChildren is not proxied: getChildren() and setChildren() are. After you load a CampWeek, the private member variables don't point to the mapped objects. They're not updated until after the correct accessor or mutator is called. The non-buggy version of this code is
Code:
public void addToChildInCampWeekSet(Child child) { getChildren().add(child); }
If you can find any code like this, fix it: all code, even code in the owning class, must access mapped properties through their accessoors and mutators only.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 23, 2006 7:41 pm 
Beginner
Beginner

Joined: Fri Feb 24, 2006 1:18 pm
Posts: 25
The code for addToChildInCampWeekSet is shown below (I didn't write it, so I guess it came from HibernateSynchronizer).
Code:
public void addToChildInCampWeekSet (camp.ChildInCampWeek childInCampWeek) {
  if (null == getChildInCampWeekSet()) setChildInCampWeekSet(new java.util.HashSet());
  getChildInCampWeekSet().add(childInCampWeek);
}


Is Hibernate Synchronizer generating the wrong code? The interesting thing is that the database is getting updated correctly (I posted a new thread on that topic today, I hope that isn't too confusing).

Also, how can I award you credits for your help?

Thanks!
RB


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 23, 2006 7:55 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
That code looks good to me. I'd trace into it to find out more, though you should add in a few newlines so that it's more obvious what's happening in the debugger. I'll go find your other topic for more info.

Click "Yes" on helpful messages to award credits.


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