How to delete child object in NHibernate? - nhibernate

I have a parent object which has a one to many relationship with an IList of child objects. What is the best way to delete the child objects? I am not deleting the parent. My parent object contains an IList of child objects. Here is the mapping for the one to many relationship:
<bag name="Tiers" cascade="all">
<key column="mismatch_id_no" />
<one-to-many class="TGR_BL.PromoTier,TGR_BL"/>
</bag>
If I try to remove all objects from the collection using clear(), then call SaveOrUpdate(), I get this exception:
System.Data.SqlClient.SqlException: Cannot insert the value NULL into column
If I try to delete the child objects individually then remove them from the parent, I get an exception:
deleted object would be re-saved by cascade
This is my first time dealing with deleting child objects in NHibernate. What am I doing wrong?
edit: Just to clarify - I'm NOT trying to delete the parent object, just the child objects. I have the relationship set up as a one to many on the parent. Do I also need to create a many-to-one relationship on the child object mapping?

You are getting the first error because, when you remove the items from the collection, NHibernate's default mode of operation is to simply break the association. In the database, NHibernate tries to set the foreign key column on the child row to null. Since you do not allow nulls in that column, SQL Server raises the error. Clearing the collection will not necessarily delete the child object, but one way to do so is to set cascade=all-delete-orphan. This informs NHibernate that it should delete the newly orphaned rows instead of setting the foreign key column.
You are getting the second error because when you call SaveOrUpdate NHibernate first deletes all of the child objects. Then, because neither relationship is marked as inverse, NHibernate also tries to set the foreign key column in your child table to null. Since the rows have already been deleted, you receive the second error. You need to set inverse=true on one side of your relationship to fix this. This is usually done on the one (primary key or parent) side. If you do not do this, NHibernate will make the appropriate updates for each side of the relationship. Unfortunately, running two updates is not the appropriate thing to do.
You should always mark one side of your relationships as the inverse side. Depending on how you code, you may or may not need to use cascading. If you want to take advantage of one shot deletes as you are trying to do using Clear(), you need to define your cascade.

Acording to Chuck's answer, I've resolved my problem by adding Inverse = true in parent side mapping:
Message has many MessageSentTo:
[HasMany(typeof(MessageSentTo), Cascade = ManyRelationCascadeEnum.AllDeleteOrphan, Inverse = true)]
public IList<MessageSentTo> MessageSendTos
{
get { return m_MessageSendTo; }
set { m_MessageSendTo = value; }
}
I am using Castle ActiveRecord. Thank you Chuck.

Try using merge() instead of saveOrUpdate(). Also, make sure your cascade is set to all-delete-orphan and that your parent-child relationship is invertible (inverse=true on the parent and then a field in the child that is the parent-id with not-null=true).

In our example we have categories with many products where a product is not nullable.
You can work around the problem by deleting the product and removing it from the parent's collection before the flush but we're still looking for a better solution to this.
product = pRepo.GetByID(newProduct.ProductID);
product.Category.Products.Remove(product);
pRepo.Delete(product);
Hope it helps anyway

Change cascade attribute value from "all" to "all-delete-orphan".

set Not-Null = true in your mapping on the column causing the issue. I'm not sure of the exact syntax though (sorry).

Related

Is it possible to delete a collection of root entities with all their children using NHibernate

Here's my problem: I have a set of ids. These are the ids of a collection of root entities. Now I want to delete all these root entities, efficiently.
I can't do a WHERE Id IN (1, 2, 3) type of clause, as I'm deleting root entities with children.
I'm wondering if it's possible to avoid retrieving all root entities and deleting them one by one. The problem with that approach isn't so much the SELECT, it's have lots of separate DELETE statement.
Is it possible for NHibernate to batch this, including the delete of all the children? Extra complexity: children can have their own children.
So I'd want NHibernate to first delete the 'bottom-most' children with an IN-clause (maybe multiples ones), then the children with an IN-clause, and then finally the root entities with an IN-clause.
If this isn't possible, what's a good approach to delete multiple root entities efficiently with NHibernate?
UPDATE
This is not valid in your case where you had a list of ids (didn't read your question properly).
First, batch deletes of the "parents" can be made setting adonet.batch_size, http://nhibernate.info/doc/nh/en/index.html#performance-batch-updates.
END UPDATE
Secondly, to avoid reading the children (and generate deletes for these), you can set the cascading delete to happen on db level by setting ON CASCADE DELETE as a foreign key constraints.
Eg (note the on-delete attr)
<set name="TheCollection" inverse="true" cascade="save-update">
<key name="theKey" on-delete="cascade"/>
<one-to-many class="TheType"/>
</set>
Note that
this only works on inverse (bidirectional) relationships.
your db schema needs to be updated with this fk constraint
don't use cascade="all" or similar where deletes are included
Might help using the Session.Delete statement passing an array of IDs? I believe this will delete the whole map for those objects matching the IDs in the array.
Session.Delete("from MyTable t where t.ID in :IDs", IDs, NHibernate.NHibernateUtil.Guid);
where IDs is an array of Guids in this case, but could be any object.

NHibernate One-to-Many - why is it updating child with null foreign key?

I am troubleshooting code that is attempting to update a disconnected entity that has uninitialized references to child entities. The intent is to update only the properties on Parent without loading children.
HasMany(x => x.ChildEntities)
.KeyColumn("ChildEntityId")
.Table("ChildEntity")
.Not.LazyLoad()
.Inverse()
.Cascade.All().AsBag();
When Session.Update(parent) is called, two update statements are executed. The first updates the parent object as expected.
update Parent set ... where ParentId = 12345
The second update confuses me...
update ChildEntity set ParentId = null where ParentId = 12345
Why is NHibernate issuing that second SQL statement? I realize that ChildEntities is uninitialized and that NHibernate is probably trying to enforce the state of Parent but I can't seem to tweak the mapping to not make this second update. I've tried Merge, lazy loading, various cascade options, etc. without success. The only connected entity in the session when it tries to commit is Parent.
Note that I typically approach this by retrieving the entity with lazy loading enabled and then mapping from the disconnected object (DTO or entity) to the connected entity before letting NHibernate persist to the database. I want to understand why the above isn't working before I suggest an alternative approach.
This was annoying.
A quick search through the NHibernate source for "could not delete collection" showed up in a block that could only execute if !isInverse (AbstractCollectionPersister.cs). That drew my attention because the mapping code was explicitly setting Inverse on that collection.
If Inverse is false and the collection is empty, NH executes an update on the child table setting the foreign key to null where the foreign key equals the parent id.
Fluent is configured to auto-map all entities in a given namespace. The assumption was that anything with a manual mapping would be ignored by auto mapping. A quick check of the hbm.xml files produced by Fluent confirmed that Inverse was not being set. I added Parent to the list of entities that were explicitly excluded from Auto Mapping and everything started working.
.IgnoreBase<Parent>()

Why doesn't NHibernate delete orphans first?

I'm trying to figure out why NHibernate handles one-to-many cascading (using cascade=all-delete-orphan) the way it does. I ran into the same issue as this guy:
Forcing NHibernate to cascade delete before inserts
As far as I can tell NHibernate always performs inserts first, then updates, then deletes. There may be a very good reason for this, but I can't for the life of me figure out what that reason is. I'm hoping that a better understanding of this will help me come up with a solution that I don't hate :)
Are there any good theories on this behavior? In what scenario would deleting orphans first not work? Do all ORMs work this way?
EDIT: After saying there is no reason, here is a reason.
Lets say you have the following scenario:
public class Dog {
public DogLeg StrongestLeg {get;set;}
public IList<DogLeg> Legs {get;set;
}
If you were to delete first, and lets say you delete all of Dog.Legs, then you may delete the StrongestLeg which would cause a reference violation. Hence you cannot DELETE before you UPDATE.
Lets say you add a new leg, and that new leg is also the StrongestLeg. Then you must INSERT before you UPDATE so that the Leg has an Id that can be inserted into Dog.StrongestLegId.
So you must INSERT, UPDATE, then DELETE.
Also as nHibernate is based on Hibernate, I had a look into Hibernate and found several people talking about the same issue.
Support one-to-many list associations with constraints on both (owner_id, position) and (child_id)
Non lazy loaded List updates done in wrong order, cause exception
wrong insert/delete order when updating record-set
Why does Hibernate perform Inserts before Deletes?
Unidirection OneToMany causes duplicate key entry violation when removing from list
And here is the best answer from them:
Gail Badner added a comment - 21/Feb/08 2:30 PM: The problem arises when a new
association entity with a generated ID
is added to the collection. The first
step, when merging an entity
containing this collection, is to
cascade save the new association
entity. The cascade must occur before
other changes to the collection.
Because the unique key for this new
association entity is the same as an
entity that is already persisted, a
ConstraintViolationException is
thrown. This is expected behavior.

nhibernate mapping: delete collection, insert new collection with old IDs

my issue lokks similar to this one: (link)
but i have one-to-many association:
<set name="Fields" cascade="all-delete-orphan" lazy="false" inverse="true">
<key column="[TEMPLATE_ID]"></key>
<one-to-many class="MyNamespace.Field, MyLibrary"/>
</set>
(i also tried to use )
this mapping is for Template object. this one and the Field object has their ID generators set to identity.
so when i call session.Update for the Template object it works fine, well, almost:
if the Field object has an Id number, UPDATE sql request is called, if the Id is 0, the INSERT is performed. But if i delete a Field object from the collection it has no effect for the Database. I found that if i also call session.Delete for this Field object, everything will be ok, but due to client-server architecture i don't know what to delete.
so i decided to delete all the collection elements from the DB and call session.Update with a new collection. and i've got an issue: nhibernate performs the UPDATE operation for the Field objects that has non-zero Id, but they are removed from DB!
maybe i should use some other Id generator or smth..
what is the best way to make nhibernate perform "delete all"/"insert all" routine for the collection?
Is the entity you are updateing already associated with the session? (ie do you load the entity and modify that loaded instance)?
It sound like you are trying to tell nhibernate to update a detached entity, in this case nhiberante cannot know what entities as been added/removed in the collection. In this case you could use Merge:
var mergedEntity = session.Merge(entityPasedFromClient)
The merge operation will fetch the enity from the db compare it with the one that as been sent from the client and merge them, that way the entity that nhiberante fetch from the db (and is associated with the session) is modified and later fetched, the merged entity is returned (this will not be the same instance as the entity you pass the merge operation).
I am not sure I understand the last part of your question:
"so i decided to delete all the collection elements from the DB and call session.Update with a new collection. and i've got an issue: nhibernate performs the UPDATE operation for the Field objects that has non-zero Id, but they are removed from DB!"
Are the field items updated and then removed?

NHibernate many-to-many delete causing foreign key violation

I'm having a problem with a pretty simple setup in NHibernate. (I'm using Fluent Nhibernate)
I have two objects as follows, setup with a bi-directional many-to-many mapping.
Project
-- Categories (IList)
Category
-- Projects (IList) -- Inverse = True
This models as expected in the db.
If I try to delete a project NHibernate performs the delete in the many-to-many table then deletes the project as expected.
However, if I try to delete a category NHibernate throws an exception that it would violate a foreign key constraint.
I've experimentet with inverse="true" on both sides but the exception is thrown either when I try to delete a Project or a Category (depending on where inverse="true" is).If I remove inverse="true" from both sides the delete works as expected on either end. But this causes double entries when saving and updating.
Can anyone tell me where I am going wrong?
Is it possible that you're not synching up your entire object graph? The schema you show suggests that Project has a collection of Categories, and Category has a collection of Projects. Hibernate expects you to keep the associations in synch within your object graph. In order to delete a Category (for example), try first clearing its projects collection, and removing that category from the "categories" collections of any projects it was associated with.