In my application users cannot truly delete records. Rather, the record's Deleted field gets set to 1, which hides it from selects.
I need to maintain this behaviour and I'm looking into whether NHibernate is appropriate for my app. Can I override NHibnernate's delete behaviour so that instead of issuing DELETE statements, it issues UPDATES, as described above?
I would obviously also need to override its SELECT behaviour to include the 'AND Deleted = 0' clause. Or read from a view instead. I'm not sure.
To implement a soft delete just bypass the Hibernate delete mechanism. Instead, map your table's Deleted field to a .Net boolean property by the same name. To delete an item, set item.Deleted = true. Then add a where attribute to your class mapping to filter out the deleted items. If you like, create another mapping for deleted items. Otherwise they will become invisible to your application, but maybe that's what you want.
Edit: Here is perhaps a better approach: use the <sql-delete> tag to write a custom delete operation for your mapping. See http://docs.jboss.org/hibernate/core/3.3/reference/en/html/querysql.html#querysql-cud. I think this in combo with the where attribute would be just the ticket. For example:
<class name="MyClass" table="my_table" where="deleted=0">
...
<sql-delete>UPDATE my_table SET deleted=1 WHERE id=?</sql-delete>
</class>
I implemented this using INSTEAD OF DELETE triggers on SQL Server to set a delete flag bit when a SQL DELETE is issued. This has two benefits: 1) I can just issue deletes from my app. without worry and 2) It enforces soft deletes for all database access (i.e. the trigger must be temporarily disabled to hard delete). I then set up views that select the active records only and mapped NHibernate to those. This solution has worked very well for me.
I think the best way to get such behaviour would be by implementing the IInterceptor interface which would allow you to perform your own code as shown within the NHibernate Documentation.
Otherwise, you could simply create a trigger on delete that would perform an update. This solution is simpler, but is this suitable for your needs?
As for the SELECT, you only need to write method that will use Criterion with a Where clause to specify the Deleted=0 thing.
Related
I have a scenarios where Table A has reference to Table B , Table B has a reference on Table C column ...etc.
To implement an update task in my project I ought to implement it in two phase logic
i.e. delete the row first and add the latest again.
But unfortunately , when I try to delete a row in table A it has reference which in turn has reference to other table and so on. Hence my logic of delete and add does not work in a proper way all the time. Even if it is deleted and added again , the sequence at which it is being added is last i.e. as a new record. Hence I am losing all the earlier references track in the same order as old one.
Hence I would like to delete a row from a table without effecting the references i.e. for time being it should allow to ignore reference , once i added it again i.e update record then i need to re-enforce/enable back the reference.
Is it possible to do in such a way ? or there any other logic works in similar fashion or replace the original intention ? could anyone please provide your expertise advice on this ?
How general logic of windows service pack works ? can any one elaborate on that? or share some info or doc or blog regarding the same?
Thank you so much.
Regards,
Shyam
What you want to do is a bad practice, I would rethink your design. It doesn't let you delete the parent record because there are child records. That is what the database ii supposed to and to try to circumvent it is a 100% guarantee of bad data.
If what you are trying to accomplish is to move the child records to a new parent, that can be done but you add the new record first and then make updates. It is best if you have some field to be able to define what old record it used to be associated with or a mapping table to use to make many changes. Then you would need to run updates for every child table. This kind of thing shoudl be a one time change, not a regular practice. It certainly shoudl virtually never happen from the application and shoudl only be done by a qualified databse developer.
If what you are trying to accomplish is to inactivate the parent so it can no longer be used for some purposes(such as creating new orders) and leave the details for reporting (wouldn't want to lose the finacials for old orders), then you should put an active flag on the table and use that to filter records instead. Often this means creating a view of only active records and pointing the code to the view insted of directly to the table.
I have an entity which contains several sets/bags.
is there any recommendation from NHibernate on how to delete it?
of course I can do foreach on each list and delete each child, but that will create many delete statements. is it better to create HQL for each child table, or maybe other approach?
I've also seen on another thread to use IStatelessSession. is it wise here?
Personally I think HQL works well in this instance.
Or if your stomach can take it use cascading deletion at the database level then delete the parent and the children precede automatically.
I have an object hierarchy as follows:
Object
Reports
Report Items
Based on other posts here, I set the Report.ReportItems mapping to Inverse, and CascadeAllDeleteOrphan. This makes it so when I remove a Report from an Object it goes to erase the ReportItems in that report.
The way it does it is very inefficient. It basically does a Delete ... Where ReportItemID = ?.
I've seen suggestions to increase the batch size to prevent this from being done in too many round trips, but that seems like a sloppy fix. Is there a way to make NHibernate generate a query like this instead:
Delete ... Where ReportID = ?
This way it would execute one query that would delete all the ReportItems instead of one statement per ReportItem.
Thanks in advance.
Edit
I have heard from a few people that NHibernate will simply not work this way with standard QueryOver etc.
I decided to start using HQL to fix the problem. I will later use some reflection to make sure there are no "magic strings" being used.
I had the idea to:
Use HQL to mass-delete the Report Items
Tell the Session to Refresh the object so it could detect that the Report Items are gone
Then tell the session to erase the Report, and let it clean up the remaining information
This does not work though. You can see the code below:
Session.CreateQuery("delete ReportItem r where r.Report= :report").SetEntity("report", SelectedReport).ExecuteUpdate()
Session.Refresh(SelectedReport)
Object.Reports.Remove(SelectedReport)
Session.Delete(SelectedReport)
Session.Update(Object)
I have also tried doing a Session.Evict after performing the HQL statements, but NHibernate keeps using the cache to try to delete the ReportItems. Any tips on how to do this?
If your foreign key has delete rule set to cascade then you can tell NH not to generate DELETE statements for child items when the parent is deleted. In this case NH will generate DELETE statement only for parent entity.
<bag name="ReportItems" cascade="all-delete-orphan" inverse="true">
<key column="report_id" on-delete="cascade" />
<one-to-many class="ReportItem" />
</bag>
After a lot of research I have found that this is simply not possible. You can use HQL, raw SQL statements, or rely on database rules to perform the deletes by enforcing a relationship. However, there is no way to tell NHibernate to delete a list of objects based on their owner's ID.
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.
looking at an existing NHibernate implementation that maps a single class to two database tables that are joined. The current functionality is read-only. The table join is actually hidden away via a readonly view and it's the view that's referred to in the NHibernate mapping. Works fine for readonly behaviour. Except I need to add Insert, Update, Delete behaviour and only one of the tables needs to be inserted/updated/deleted. How best to do this? I can take readonly off the view of course, I could replicate the join in the NHibernate mapping, but how do I tell NHibernate that insert/update/delete affects one table only?
Thanks DC
You can set the update and insert attributes on the property mappings to false and they will be excluded from updates and inserts:
<property name="MyProperty" update="false" insert="false" />
Keep in mind that your view will need to have a single primary key defined to enable updates or inserts.
You can map to a view instead of a table, then define custom sql for your inserts, updates and deletes.
I would recommend calling a stored procedure.
See the docs: http://nhibernate.info/doc/nh/en/index.html#querysql-cud
I've used this technique quite a lot and it works fine.