persiste the deletion operation from a collection in NHibernate - nhibernate

When i delete some object (or remove it) from a collection (such as list) and call SaveOrUpdate from the parent of this collection the row of the child isn't removed but updated by setting the foreign key value to NULL.
How can i force it to be deleted (the child row).

You need to mark the collection as the inverse side of the relationship and set the cascade setting to all-delete-orphan. NHibernate is attempting to update the foreign key to null because you have created an orphan by removing it from the collection but haven't instructed it to delete orphans.

Related

Delete records from relational database with no cascade involved

I need to delete records from relational database, where I attempt to start from the lowest children in the database.
I'm not very strong on how to approach the task. I don't want to do CASCADE delete, I actually want to do the opposite of CASCADE.
Is is correct that I have to find the entity that does not have child and start deleting the records there? and what if an entity has more that one foreign key, how do I decide on which parent table should I start to delete from?
You have to delete the child records first. If you try to delete a record that is referenced with a foreign key, you will get an error which should indicate which key has a conflict. You can then see which child table is impacted and delete the records that are referencing the foreign key, then try again.
You simply work your way up the chain. If more than one child record references a parent record, you simply delete all the child records first. If more than one parent record is referenced by a child record, it doesn't matter which parent is deleted first (or if they are deleted at all).
You don't give what database and what tools you have at hand.
You could manually diagram the database based on foreign keys or you could use a tool, such as visual studios to diagram your database.
As long as the multiple foreign relationships don't depend on one another it shouldn't matter where you start.

fluent nhibernate one to many parent delete

We have 2 classes, Parent and Child
The parent has no reference to the child, the child has the following defined in its fluent mapping:
References(x => x.Parent, "Parent_id").Not.Nullable();
When the parent record is deleted, the following error is generated:
The DELETE statement conflicted with the REFERENCE constraint "FKFF68C21EE06905B9". The conflict occurred in database "DatabaseName", table "dbo.tblChild", column 'Parent_id'.
The statement has been terminated.
What would be the correct mapping to enable the deletion of the parent, given that the parent model has no property collection of type child?
You cannot delete parent records, that are referenced by child records because of the foreign key constraint. If you do not want to map the children as a collection reference, you will have to delete the reference to the parent record in all child record before deleting the parent record. You can do so by setting the reference to the parent to null (removing your not null constraint) or by deleting the child record.
All other solutions include an inverse child collection in your parent record with a cascade mapping.

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

Constraint for one-to-many relationship

We have a two tables with a one-to-many relationship. We would like to enforce a constraint that at least one child record exist for a given parent record.
Is this possible?
If not, would you change the schema a bit more complex to support such a constraint? If so how would you do it?
Edit: I'm using SQL Server 2005
Such a constraint isn't possible from a schema perspective, because you run into a "chicken or the egg" type of scenario. Under this sort of scenario, when I insert into the parent table I have to have a row in the child table, but I can't have a row in the child table until there's a row in the parent table.
This is something better enforced client-side.
It's possible if your back-end supports deferrable constraints, as does PostgreSQL.
How about a simple non nullable column?
Create Table ParentTable
(
ParentID
ChildID not null,
Primary Key (ParentID),
Foreign Key (ChildID ) references Childtable (ChildID));
)
If your business logic allows and you have default values you can query from the database for each new parent record, you can then use a before insert trigger on the parent table to populate the non nullable child column.
CREATE or REPLACE TRIGGER trigger_name
BEFORE INSERT
ON ParentTable
FOR EACH ROW
BEGIN
-- ( insert new row into ChildTable )
-- update childID column in ParentTable
END;
This isn't really something 'better enforced on the client side' so much as it is something that is impractical to enforce within certain database implementations. Realistically the job DOES belong in the database and at least one of the workarounds below should work.
Ultimately what you want is to constrain the parent to a child. This guarantees that a child exists. Unfortunately this causes a chicken-egg problem because the children must point to the same parent causing a constraint conflict.
Getting around the problem without visible side-effects in the rest of your system requires one of two abilities - neither of which is found in SQL Server.
1) Deferred constraint validation - This causes constraints to be validated at the end the transaction. Normally they happen at the end of a statement. This is the root of the chicken-egg problem since it prevents you from inserting either the first child or the parent row for lack of the other and this resolves it.
2) You can use a CTE to insert the first child where the CTE hangs off of the statement that inserts the parent (or vise versa). This inserts both rows in the same statement causing an effect similar to deferred constraint validation.
3) Without either you have no choice but to allow nulls in one of the references so you can insert that row without the dependency check. Then you must go back and update the null with the reference to the second row. If you use this technique you need to be careful to make the rest of the system refer to the parent table thru a view that hides all rows with null in the child reference column.
In any case your deletes of children are just as complicated because you cannot delete the child that proves at least one exists unless you update the parent first to point to a child that won't be deleted.
When you are about to delete the last child either you must throw an error or delete the parent at the same time. The error will occur automatically if you don't set the parent pointer to null first (or defer validation). If you do defer (or set the child pointer to null) your delete of the child will be possible and the parent can then be deleted as well.
I literally researched this for years and I watch every version of SQL Server for relief from this problem since it's so common.
PLEASE As soon as anyone has a practical solution please post!
P.S. You need to either use a compound key when referring to your proof-of-child row from the parent or a trigger to insure that the child providing proof actually considers that row to be its parent.
P.P.S Although it's true that null should never be visible to the rest of your system if you do both inserts and the update in the same transaction this relies on behavior that could fail. The point of the constraint is to insure that a logic failure won't leave your database in an invalid state. By protecting the table with a view that hides nulls any illegal row will not be visible. Clearly your insert logic must account for the possibility that such a row can exist but it needs inside knowledge anyway and nothing else needs to know.
I am encountering this issue, and have a solution implemented in Oracle rel.11.2.4.
To ensure that every child has a parent, I applied a typical foreign-key constraint from the FK of the child to the PK of the parent. -- no wizardry here
To ensure that every parent has at least one child, I did as follows:
Create a function which accepts a parent PK, and returns a COUNT of children for that PK. -- I ensure that NO_DATA_FOUND exceptions return 0.
Create a virtual column CHILD_COUNT on the parent table and calculate it to the function result.
Create a deferrable CHECK constraint on the CHILD_COUNT virtual column with the criteria of CHILD_COUNT > 0
It works as follows:
If a parent row is inserted and no children exist yet. then if that row is committed, the CHILD_COUNT > 0 CHECK constraint fails and the transaction rolls back.
If a parent row is inserted and a corresponding child row is inserted in the same transaction; then all integrity constraints are satisfied when a COMMIT is issued.
If a child row is inserted corresponding to an existing parent row, then the CHILD_COUNT virtual column is recalculated on COMMIT and no integrity violation occurs.
Any deletes of parent rows must cascade to the children otherwise orphaned child rows will violate the FOREIGN KEY constraint when the delete transaction is committed. (as usual)
Any deletes of child rows must leave at least one child for each parent otherwise the CHILD_COUNT check constraint will violate when the transaction commits.
NOTE: that I would not need a virtual column if Oracle would allow user-function-based CHECK constraints at rel.11.2.4.

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