-->
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.  [ 13 posts ] 
Author Message
 Post subject: Another Newbie question. <bag cascade="...">
PostPosted: Sun Apr 08, 2007 3:04 am 
Beginner
Beginner

Joined: Tue Feb 13, 2007 9:29 pm
Posts: 21
1.2 sql server 2000.

Hi,

I was trying to delete a children element of a parent (one-to-many), and I got "deleted object would be re-saved by cascade", then i noticed in my parent mapping, I had a bag of children element with cascade="all", so I changed to "none" and the error is gone.

but this caused another problem. When I try to save the parent (and attached the children along with it), i get

"object references an unsaved transient instance - save the transient instance before flushing"

hm.. okay. So I changed the casecade="save-update" (and also tried "all-delete-orphan", and now saving parent(with children attached to it), agian it's fine, but back to delete children again, get the original error "deleted object would be re-saved by cascade".

I went back to documentation and now I'm a bit confused.. how do I delete the chidlren? and when should I use cascade="all" v.s "save", "delete" and etc?

From page 79 of the pdf reference guide

"Mapping an association (many-to-one, or collection) with cascade="all" marks the association as a parent/
child style relationship where save/update/deletion of the parent results in save/update/deletion of the
child(ren). Futhermore, a mere reference to a child from a persistent parent will result in save / update of the
child. The metaphor is incomplete, however. A child which becomes unreferenced by its parent is not automatically
deleted, except in the case of a <one-to-many> association mapped with cascade="all-delete-orphan"

here is my parent mapping file

Code:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="Jumptree.Forum.BusinessEntities"
                   namespace="Jumptree.Forum.BusinessEntities"
                   >
  <class name="JumptreeForum_Discussions" lazy="false">
    <id name="DiscussionID">
      <generator class="native" />
    </id>
    <property name="DiscussionTitle" />
    <property name="DiscussionLastPostedOn" />
    <property name="DiscussionDefaultComment" />
    <property name="CreatedBy" />
    <property name="CreatedByIP" />
    <property name="CreatedOn" />
    <property name="UpdatedBy" />
    <property name="UpdatedOn" />
    <bag name="Categories" table="JumptreeForum_DiscussionsCategories" lazy="true">
      <key column="DiscussionID" />
      <many-to-many class="JumptreeForum_Categories" column="CategoryID" />
    </bag>
    <bag  name="Comments"
          order-by="CreatedOn"
          lazy="true"
          cascade="none" >
      <key column="DiscussionID" />
      <one-to-many class="JumptreeForum_DiscussionComments" />
    </bag>
  </class>
</hibernate-mapping>




here is my child mapping

Code:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="Jumptree.Forum.BusinessEntities"
                   namespace="Jumptree.Forum.BusinessEntities"
                   >
  <class name="JumptreeForum_DiscussionComments" lazy="false">
    <id name="DiscussionCommentID">
      <generator class="native" />
    </id>
    <property name="DiscussionID" />
    <property name="DiscussionComment" />
    <property name="CreatedBy" />
    <property name="CreatedByIP" />
    <property name="CreatedOn" />
    <property name="UpdatedBy" />
    <property name="UpdatedOn" />
  </class>
</hibernate-mapping>



and here is the code I used to delete the children


Code:

....

   JumptreeForum_DiscussionComments comment = (JumptreeForum_DiscussionComments)
                                               ExampleApplication.GetCurrentSession()
                                               .Load(typeof(JumptreeForum_DiscussionComments), Convert.ToInt32(discussionCommentId));
                        ExampleApplication.GetCurrentSession().Delete(comment);
                        ExampleApplication.GetCurrentSession().Flush();





Thanks


Top
 Profile  
 
 Post subject:
PostPosted: Sun Apr 08, 2007 4:12 am 
Beginner
Beginner

Joined: Tue Feb 13, 2007 9:29 pm
Posts: 21
okay.. looking at section "16.3. Cascading lifecycle"..

i did two thigns

1. change cascade to "all-delete-orphan" in the parent mapping

Code:


<bag  name="Comments"
          order-by="CreatedOn"
          lazy="true"
          cascade="all-delete-orphan" >
      <key column="DiscussionID" />
      <one-to-many class="JumptreeForum_DiscussionComments" />
    </bag>



2. I first retrieved the parent object (JumptreeForum_Discussion) and then loop through all its children, find the child element, remove from the parent and then delete the child...

Code:

JumptreeForum_Discussions discussion = (JumptreeForum_Discussions)
                                                               ExampleApplication.GetCurrentSession()
                                                               .Load(typeof(JumptreeForum_Discussions), Convert.ToInt32(Request["discussionid"]));

                        foreach (JumptreeForum_DiscussionComments comment in discussion.Comments)
                        {
                            if (comment.DiscussionCommentID == Convert.ToInt32(discussionCommentId))
                            {
                                discussion.Comments.Remove(comment);
                                ExampleApplication.GetCurrentSession().Delete(comment);
                                ExampleApplication.GetCurrentSession().Flush();
                                break;
                            }
                        }



this worked.. but I'm just not sure if i'm doing it right.. to delete this child, from log, i see 4 sql exectued for this one operation

1. one sql select to get parent..
2. one sql select to get the children elements of the parent
3. one sql update to set the children element's parentid to null
4. one sql delete to delete the children element

is that right???? 4 sql to delete one record?


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 19, 2007 5:56 am 
Beginner
Beginner

Joined: Thu Apr 19, 2007 5:37 am
Posts: 28
I've got the same problem here, I don't understand why I get an update query before the deletion, which at the same time, tries to "null" the parentid and the index column of my MAP collection.

That's expensive, and I get a "constrain error" because obviously, I don't want my index column to be "null".

Tito22102 wrote:
this worked.. but I'm just not sure if i'm doing it right.. to delete this child, from log, i see 4 sql exectued for this one operation

1. one sql select to get parent..
2. one sql select to get the children elements of the parent
3. one sql update to set the children element's parentid to null
4. one sql delete to delete the children element

is that right???? 4 sql to delete one record?


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 19, 2007 8:20 am 
Expert
Expert

Joined: Thu Dec 14, 2006 5:57 am
Posts: 1185
Location: Zurich, Switzerland
1) If you want to retrieve the parent and child objects in a single statement, you have to add fetch="join" or fetch="subselect" to your bag mapping. But, then you can't use lazy fetching.

2) If you want to avoid the update, you have to add a parent mapping to your child mapping:

<many-to-one name="Discussion" column="discussionid" / >

and add inverse="true" to the bag in the parent class. The

See chapter 17 in the reference manual:

Quote:
The underlying cause is that the link (the foreign key parent_id) from p to c is not considered part of the state
of the Child object and is therefore not created in the INSERT. So the solution is to make the link part of the
Child mapping.


Hope that helps,

Wolfgang


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 19, 2007 9:22 am 
Beginner
Beginner

Joined: Thu Apr 19, 2007 5:37 am
Posts: 28
Thank you, that kind of helps, but:

1- I am using a "map" which for makes more sense, since I want to be able to access specific objects easily, and maps can't have bi-directional relationships

2- I just found this in the Hibernate (not NHibernate) documentation:

Code:
<key
column="columnname" (1)
on-delete="noaction|cascade" (2)
property-ref="propertyName" (3)
not-null="true|false" (4)
update="true|false" (5)
unique="true|false" (6)
/>

(1) column (optional): The name of the foreign key column. This may also be specified by nested <column>
element(s).

(2) on-delete (optional, defaults to noaction): Specifies whether the foreign key constraint has databaselevel
cascade delete enabled.

(3) property-ref (optional): Specifies that the foreign key refers to columns that are not the primary key of
the orginal table. (Provided for legacy data.)

(4) not-null (optional): Specifies that the foreign key columns are not nullable (this is implied whenever the
foreign key is also part of the primary key).

(5) update (optional): Specifies that the foreign key should never be updated (this is implied whenever the
foreign key is also part of the primary key).

(6) unique (optional): Specifies that the foreign key should have a unique constraint (this is implied whenever the foreign key is also the primary key).

We recommend that for systems where delete performance is important, all keys should be defined on-delete="
cascade", and Hibernate will use a database-level ON CASCADE DELETE constraint, instead of many individual
DELETE statements. Be aware that this feature bypasses Hibernate's usual optimistic locking strategy for
versioned data.

The not-null and update attributes are useful when mapping a unidirectional one to many association. If you
map a unidirectional one to many to a non-nullable foreign key, you must declare the key column using <key
not-null="true">.


It seems like the not-null attribute is what i need, but it also seems like it doesn't exist in NHibernate??


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 19, 2007 9:27 am 
Beginner
Beginner

Joined: Thu Apr 19, 2007 5:37 am
Posts: 28
And also, I would like to be able to have the "index" key as not null.
I have already mapped them as "not-null" in the class mapping of the contained elements, but NHibernate doesn't seem to take it into consideration.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 19, 2007 9:37 am 
Expert
Expert

Joined: Thu Dec 14, 2006 5:57 am
Posts: 1185
Location: Zurich, Switzerland
Quote:
1- I am using a "map" which for makes more sense, since I want to be able to access specific objects easily, and maps can't have bi-directional relationships


Afaik, the parent child mapping shoud work with maps exactly the way it does for bags or lists. Have you tried it ?

Quote:
And also, I would like to be able to have the "index" key as not null.
I have already mapped them as "not-null" in the class mapping of the contained elements, but NHibernate doesn't seem to take it into consideration.


If you remove the child from the collection, hibernate has to set the column (key and/or index) to null otherwise the item would still be part of the collection.

If it does not work, can you post your mappings and the code you use for deleting the child.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 19, 2007 10:19 am 
Beginner
Beginner

Joined: Thu Apr 19, 2007 5:37 am
Posts: 28
1 - I haven't tried, you are right. I'll give it a try if I can't find any other way to solve the issue


2 - You are also right about setting the key and/or index to null, but I don't understand why it has to set them to null before deleting them. Wouldn't it make sense to just delete them without the "UPDATE" query first?


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 19, 2007 10:36 am 
Expert
Expert

Joined: Thu Dec 14, 2006 5:57 am
Posts: 1185
Location: Zurich, Switzerland
Have a look in chapter 17 of the reference, it's explained there quite well. Removing an object from a collection does not delete the object automatically. You have to delete the child explicitly or use cascade="all-delete-orphan" on the collection mapping. But I think, even then, you have to set the parent link in the child object to null (if you have one) before you commit your changes.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 19, 2007 10:43 am 
Beginner
Beginner

Joined: Thu Apr 19, 2007 5:37 am
Posts: 28
I have a "all-delete-orphans" cascade value.

Let me give you some idea about what code I have:


the container class contains a dictionary of contained object.

i just do this:

transaction = session.begintransaction()

container = session.get<containerclass>(id)

container.clear() //clears the dictionary

container.add(object) in a "loop" to repopulate the container with new objects.

transaction.commit()

and here, I get plenty of

updating with null values
inserting the new values
deleting the rows which had been updaetd just before.

I would like to know if it is possible to just automatically insert the new rows, delete the privious ones (knowing that they have unique ids).


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 19, 2007 10:46 am 
Expert
Expert

Joined: Thu Dec 14, 2006 5:57 am
Posts: 1185
Location: Zurich, Switzerland
yes, if you mark the collection mapping as inverse="true" and add a many-to-one mapping to your childs.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 19, 2007 11:00 am 
Beginner
Beginner

Joined: Thu Apr 19, 2007 5:37 am
Posts: 28
wolli wrote:
yes, if you mark the collection mapping as inverse="true" and add a many-to-one mapping to your childs.


Ok, i'll do it right now and see what I get.

Thanks


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 19, 2007 1:18 pm 
Beginner
Beginner

Joined: Thu Apr 19, 2007 5:37 am
Posts: 28
It really seems like you can have the inverse relationship with the MAP

Wierd because I had read the opposite...

So thanks a lot, having the reverse relationship did fix my problem of UPDATE before DELETE, though it makes me think that NHibernate is not as transparent as I believed, since I had to change my classes design.


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