Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NHibernate Definitive Cascade application guide

Are there any internet resources that have a definitive guide to all of the cascade settings for NHibernate that will include examples of the class structure, HBM and the implications of actions with each of the cascade settings for all of the relationships with NH.

Also it would be helpful if there were examples for common associations to be done in the most correct manner such as setting up a states table that you will never end up cascade deleting a state, or that deleting an object that has a CreatedBy User property will never end up deleting the User in a cascade etc.

like image 864
Chris Marisic Avatar asked Jan 13 '10 16:01

Chris Marisic


3 Answers

The following is adapted from the Java Hibernate reference http://docs.jboss.org/hibernate/stable/core/manual/en-US/html/objectstate.html#objectstate-transitive for NHiberate 3.0 (ie the current svn trunk).

For each basic operation of the NHibernate Session - including Persist(), Merge(), SaveOrUpdate(), Delete(), Lock(), Refresh(), Evict(), Replicate() - there is a corresponding cascade style. Respectively, the cascade styles are named persist, merge, save-update, delete, lock, refresh, evict, replicate. The cascade style for Save() and Update() is save-update; for SaveAndUpdateCopy() it is merge; and for PersistOnFlush() it is persist. And remove is an alias for delete.

If you want an operation to be cascaded along an association, you must indicate that in the mapping document. For example:

<one-to-one name="person" cascade="persist"/>

Cascade styles my be combined:

<one-to-one name="person" cascade="persist,delete,lock"/>

You can use cascade="all" to specify that all operations should be cascaded along the association. The default cascade="none" specifies that no operations are to be cascaded.

A special cascade style, delete-orphan, applies only to one-to-many associations, and indicates that the Delete() operation should be applied to any child object that is removed from the association. And all-delete-orphan is the same as all,delete-orphan.

Recommendations:

  • It does not usually make sense to enable cascade on a <many-to-one> or <many-to-many> association. Cascade is often useful for <one-to-one> and <one-to-many> associations.
  • If the child object's lifespan is bounded by the lifespan of the parent object, make it a life cycle object by specifying cascade="all-delete-orphan".
  • Otherwise, you might not need cascade at all. But if you think that you will often be working with the parent and children together in the same transaction, and you want to save yourself some typing, consider using cascade="persist,merge,save-update".

Mapping an association (either a single valued association, or a collection) with cascade="all" marks the association as a parent/child style relationship where save/update/delete of the parent results in save/update/delete of the child or children. 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="delete-orphan". The precise semantics of cascading operations for a parent/child relationship are as follows:

  • If a parent is passed to Persist(), all children are passed to Persist()
  • If a parent is passed to Merge(), all children are passed to Merge()
  • If a parent is passed to Save(), Update() or SaveOrUpdate(), all children are passed to SaveOrUpdate()
  • If a transient or detached child becomes referenced by a persistent parent, it is passed to SaveOrUpdate()
  • If a parent is deleted, all children are passed to Delete()
  • If a child is dereferenced by a persistent parent, nothing special happens - the application should explicitly delete the child if necessary - unless cascade="delete-orphan", in which case the "orphaned" child is deleted.
like image 122
Lachlan Roche Avatar answered Oct 14 '22 20:10

Lachlan Roche


Accepted answer explains this in details with HBM files. This answer covers the same with Mapping By Code. They are almost same; just mapped to their HBM strings.

Article form Ayende explains it well:

  • none - do not do any cascades, let the users handles them by themselves.
  • save-update - when the object is saved/updated, check the associations and save/update any object that require it (including save/update the associations in many-to-many scenario).
  • delete - when the object is deleted, delete all the objects in the association.
  • delete-orphan - when the object is deleted, delete all the objects in the association. In addition to that, when an object is removed from the association and not associated with another object (orphaned), also delete it.
  • all - when an object is save/update/delete, check the associations and save/update/delete all the objects found.
  • all-delete-orphan - when an object is save/update/delete, check the associations and save/update/delete all the objects found. In additional to that, when an object is removed from the association and not associated with another object (orphaned), also delete it.

Also, this question explains few inner implementations of Cascade.

[Flags]
public enum Cascade
{
    None = 0,
    Persist = 2,
    Refresh = 4,
    Merge = 8,
    Remove = 16,
    Detach = 32,
    ReAttach = 64,
    DeleteOrphans = 128,
    All = 256,
}
private static IEnumerable<string> CascadeDefinitions(this Cascade source)
{
    if (source.Has(Cascade.All))
    {
        yield return "all";
    }
    if (source.Has(Cascade.Persist))
    {
        yield return "save-update, persist";
    }
    if (source.Has(Cascade.Refresh))
    {
        yield return "refresh";
    }
    if (source.Has(Cascade.Merge))
    {
        yield return "merge";
    }
    if (source.Has(Cascade.Remove))
    {
        yield return "delete";
    }
    if (source.Has(Cascade.Detach))
    {
        yield return "evict";
    }
    if (source.Has(Cascade.ReAttach))
    {
        yield return "lock";
    }
    if (source.Has(Cascade.DeleteOrphans))
    {
        yield return "delete-orphan";
    }
}

Please note that Cascade.All does not include Cascade.DeleteOrphans. In that case, you may need to include it using Cascade.All | Cascade.DeleteOrphans.

Alternatively, you can use Include extension method Cascade.All.Include(Cascade.DeleteOrphans).

In combination with Cascade, you may need to look into Inverse as well; it specifies the owner of the association. Refer to this question and this answer for more details.

like image 4
Amit Joshi Avatar answered Oct 14 '22 21:10

Amit Joshi


This might be obvious advice but I would suggest browsing old post made by Ayende. A quick search for NHibernate and cascade on his site revealed a few interesting posts. They might, however, be a bit too scarce for your needs.

Even though it is not a internet resource per se, I would also recommend NHibernate in Action. It addresses cascades in some depth in chapter 3, 4 and 6. The book targets NHibernate 1.2. I do believe, though, that there will be a new edition of the book targeting the 3.0 release of NHibernate; it might be worth keeping an eye on.

As much as I would have liked to see a definitive guide to cascades I have not seen one. Maybe you could summarize some of the blog posts out there discussing cascades with your own post on your own blog.

like image 3
mrydengren Avatar answered Oct 14 '22 21:10

mrydengren