R – NHibernate many-to-one relationship deleting parent only

many-to-onenhibernatenhibernate-mapping

I have a many-to-one relationship between objects Product and Supplier. I need to be able to delete Supplier without deleting the Products that belong to it.

Here is a simplified version of classes:

public class Supplier {
    public virtual IList<Product> Products { get; protected set; }
}

public class Product {
    // Product belongs to a Category but Supplier is optional
    public virtual Supplier Supplier { get; set; }
    public virtual Category Category { get; set; }
}

I'm using FluentNHibernate, but here are the mappings it produces:

<bag name="Products" cascade="save-update" inverse="true">
      <key column="SupplierID" />
      <one-to-many class="Me.Product, Me, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</bag>

<many-to-one name="Supplier" column="SupplierID" />

This creates a foreign key on the Products table, so when I attempt to do a straight delete on a Supplier I get a foreign key constraint error. I tried changing cascade to 'all', in the hope that it might only delete the relationship, but it deleted all Products and their other associated objects.

The only way I can see to solve this right now is to iterate the Products collection of Supplier and set the Supplier property to null. Is there a way I can achieve this behaviour through the mapping?

Best Answer

The mapping properties only take effect when the entity is actually loaded, and when you are not querying through HQL. As an example, if you specify Cascade=ALL, if you delete a supplier with the query "delete from Supplier where id=:id", you will probably get the same FK constraint failure, because the hql doesn't trigger (programmatic) cascades.

It seems that the Products are the owning side of the relation, which is good. I think you have two choices:

  • Code some method on the Supplier to iterate through all Products and set the Product's supplier to null, and use this method before deleting any Suppliers
  • Before issuing a Supplier delete, make sure your DAO sets the product's supplier to null

Example:

public int Delete(Supplier s) {
    return Session.CreateQuery("udpate Product set Supplier = null where Supplier = :supplier")
        .SetParameter("supplier", s)
        .ExecuteUpdate();
}
Related Topic