views:

1384

answers:

2

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?

+2  A: 

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();
}
Miguel Ping
Hadn't thought of doing it as a query within the data access layer, this was perfect!
roryf
Turns out you can't do UPDATE or DELETE statements in HQL, only way to do this is in raw SQL. In this instance I can live with that.
roryf
Are you sure? I'm using delete all over my code:svc.getEm().createQuery("delete from UserOperatorEJB ").executeUpdate();Also check this links:http://www.java2s.com/Code/Java/Hibernate/HQLDeleteHQL.htmhttp://twasink.net/blog/2005/04/differences-in-behaviour-between-hibernate-delete-queries-and-the-old-way/Too bad the hibernate docs site is down.
Miguel Ping
I'm using NHibernate (.NET port), not Hibernate :o) There is an open issue in the JIRA to port this from HIbernate but I don't see it happening any time soon.
roryf
My bad, I thought NHibernate was a close port.
Miguel Ping
`Session.Delete` works for NHibernate 2.1 at least. But it seems I need to delete children then parents:`session.Delete("from Product where SupplierID = :id", id, NHibernateUtil.Int32);``session.Delete("from Supplier where ID = :id", id, NHibernateUtil.Int32);`
Chris Bilson
A: 

DON'T DO THIS.

You have an implicit or explicit assertion in your model that all Products have a Supplier. The foreign key is there to enforce this condition. Removing the Supplier while keeping the Products violates your model and will probably cause much of your code, which relies on this always being true, to fail.

The one thing you can do, you've already discovered: for every Product that has this Supplier, you can set the Product's Supplier null. This won't violate your condition, but it's the same as saying "we don't know who the Supplier of this Product is" and may cause code failures.

Why do you want to do this?

tpdi
The Supplier property on Product is optional, Product it is owned by another object other than Supplier. What would you suggest? A join class?
roryf