18

I have two classes A and B. Many B's can have association with a single A, hence a many-to-one relationship from B to A. I've mapped the relationship like:

<class name="A" table="tbl_A">
  <property name="propA" column="colA"/>
</class>
<class name="B" table="tbl_B">
  <property name="propB" column="colB"/>
  <many-to-one name="a" class="A" column="col1" cascade="delete"/>
</class>

A has nothing mapped to B. Keeping this in mind we intend to delete B when it's associated A is deleted. This could have been possible if I could define an inverse="true" on the many-to-one association in B but hibernate does not allow that.

Can anyone help with this? We do not want to write anything in A for this.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Monis Iqbal
  • 1,987
  • 7
  • 26
  • 42
  • I tried this myself, I don't think its possible. It may be a bug in hibernate, or hibernate may think there is a one-to-many relationship on the other side, and decide that deleting a member of a collection, should not result in the main objeect being deleted as well as all the objects in that collection as well. – Zoidberg Nov 11 '09 at 13:36
  • Can you confirm that what you want to do is to delete A and all B's associated to A? – Pascal Thivent Nov 11 '09 at 13:42
  • @Pascal yes, that's my intention – Monis Iqbal Nov 12 '09 at 05:04

2 Answers2

24

Hibernate only cascades along the defined associations. If A knows nothing about Bs, nothing you do with A will affect Bs.

Pascal's suggestion is, therefore, the easiest way to do what you want:

<class name="A" table="tbl_A">
  ...
  <set name="myBs" inverse="true" cascade="all,delete-orphan">
    <key column="col1"/>
    <one-to-many class="B"/>
  </set>
</class>

<class name="B" table="tbl_B">
  ...
  <many-to-one name="a" class="A" column="col1" not-null="true"/>
</class>

Note that setting cascade="delete" on B as you have it in your original code will NOT do what you want - it tells Hibernate to "delete A if B is deleted" which is likely to result in constraint violation (if there are any other Bs linked to that A).

If you absolutely cannot add a collection of Bs to A (though I can't really think of the circumstances where that'd be the case), your only other alternative is to define cascade delete from A to B at the foreign key level; your Bs will then be deleted when your A is deleted.

This is a rather ugly solution, however, because you have to be extremely careful of how you delete A in Hibernate:

  1. Session must be flushed prior to deleting A (having pending updates to B may result in an error or A and some Bs being re-inserted behind the scenes)
  2. All Bs linked to your A (and since you're not maintaining the relationship from A side that means all Bs) must be evicted from all active sessions and 2nd level cache.
ChssPly76
  • 99,456
  • 24
  • 206
  • 195
  • 2
    Well, +1 too for this complete and clear answer. I really need to improve the way I explain things about Hibernate in english :) – Pascal Thivent Nov 11 '09 at 18:07
  • 1
    @Pascal - thanks. I think your English is perfectly fine, BTW. Then again, it's not my first language either, so what do I know? :-) – ChssPly76 Nov 11 '09 at 19:16
  • I dont want to map collections but hibernate forces me to do so since cascading is necessary. Created an issue: https://hibernate.atlassian.net/browse/HHH-8403 – djmj Jul 31 '13 at 21:21
  • Im getting update issues with this, using the transactional spring annotation, i can save and delete rightly but update gives issues. cause = {org.hibernate.HibernateException@12398}"org.hibernate.HibernateException: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: com.x.personalpreferences.Widget.tabWidgets" – Maxrunner Jul 25 '14 at 16:54
7

I think you need to cascade="all,delete-orphan" from A to B's with a one-to-many association.

Pascal Thivent
  • 562,542
  • 136
  • 1,062
  • 1,124