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

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>()

Related

Delete in HasManyToMany

I have three table -
1. Anomaly
2. Markup
3. Anomaly_Markup
Mapping -
public AnomalyMap()
{
Table("anomaly");
Id(x => x.Id).Column("ID").CustomType("decimal");
HasManyToMany<DMMarkupData>(x => x.DMMarkupData)
.Table("anomaly_markup")
.ParentKeyColumn("ANOMALY_ID")
.ChildKeyColumn("MARK_UP_ID")
.Cascade.All()
.LazyLoad();
}
public MarkupDataMap()
{
Table("markup");
Id(x => x.Id).Column("ID");
}
Condition :
Save data by Anomaly - Anomaly contains MarkupData. It saves data. It is working functionality with me.
Delete markup - which should delete relationship from map table and markup data. I am facing this issue.
Anyone help me to find out solution, how to delete markup data ?
I see no relationship of MarkUpData with Anomaly. There must be same relationship and you should specify the control of cascade operation by using Inverse attribute in your mapping.
You can refer : How to set "cascade delete" option to "Set Null" in Fluent NHibernate?
To delete DMMarkupData just remove the object from collection and call for Save Anomaly.
From what you've posted it looks like you haven't defined a relationship on DMMarkupData -> Anomaly, so NHibernate won't know to delete it the MarkupData entries from the anomaly_markup table (despite the reverse relationship being there). You can either solve it with a database level cascade which removes entries in anomaly_markup when deleting MarkupData, or you can map the relationship in code & NHibernate and then NHibernate will do the cascade for you.
NHibernate does not manage the object graph, it only persists it. Removing the item from the list when it gets deleted is responsibility of the business logic!
(All the tricks with triggers and stuff that workaround it lead to inconsistencies and side effects within the transaction which does the change. From point of view of persistence ignorance it is not recommended. I would only do it when facing performance issues that can't be solved in another way.)
You can simplify it by using components. Provided that
you don't reference the same markup from somewhere else
you don't need to query for markup unrelated to Anomality
markups do never live outside of an Anomality
given all that, it is much easier to work with components. (I don't know how it is called in fluent, but in xml mapping it is called a "composite-element").
When using components, you don't need to delete the markup from the database. You just remove it from the list where it lives in.

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.

(Fluent) NHibernate: force foreign key to null on delete

I have a standard parent - child (1:many) relationalship, configured using Fluent NHibernate:
On the parent side:
HasMany(x => x.Items).Inverse().Cascade.All();
and on the child side:
Map(x => x.ItemCategory).Nullable().Index("idx_item_category").Not.LazyLoad()
(Edit in response to epitka's comment:)
The record is deleted by calling
session.Delete(item_category)
This is the only operation done in the transaction.
(End Edit)
Currently when I delete an ItemCategory record it cascade the delete to all the items, which appears to be working as expected according to the documentation.
What I want is for Item.ItemCategory to be set to null automatically when the ItemCategory record is deleted.
I can only seem to turn off the cascade completely, which leads to a broken database (item's referencing a missing category). So, currently I have to do this manually which is a little more error prone than I'd like.
Is it possible to configure this behaviour?
session.Delete(item_category)
Whil it's not possible to do that out of the box, you can probably implement an IPreDeleteEventListener that fires an HQL update to set the Items' ItemCategory to null.

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.

How to delete child object in 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).