[hibernate-issues] [Hibernate-JIRA] Commented: (HHH-2421) Cascading Delete In Wrong Order

Gail Badner (JIRA) noreply at atlassian.com
Thu Mar 1 22:07:33 EST 2007


    [ http://opensource.atlassian.com/projects/hibernate/browse/HHH-2421?page=comments#action_26355 ] 

Gail Badner commented on HHH-2421:
----------------------------------

If the intention is to delete c when b is deleted, then the many-to-one association that B has with C should have cascade="delete-orphan". Once this is set properly in the mapping file, b is deleted before c and there is no need to explicitly delete c.

There is another bug in the mapping file that did not manifest itself in the test. The mapping for a bidirectional one-to-many/many-to-one association is not correct. In order to make the association bidirectional, the key column for the list in A needs to match the many-to-one column in B. In the above mapping the key column for the list is "bId" and the many-to-one column is "aId".

The following corrects both of these problems:

<hibernate-mapping>
    <class name="scratchpad.hibernate.A">
        <id name="id">
            <generator class="increment"/>
        </id>
        <list name="bs" cascade="all,delete-orphan">
            <key column="aId"/>
            <list-index column="idx"/>
            <one-to-many class="scratchpad.hibernate.B"/>
        </list>
    </class>
    <class name="scratchpad.hibernate.B">
        <id name="id">
            <generator class="increment"/>
        </id>
        <many-to-one name="a" column="aId" insert="false" update="false"/>
        <many-to-one name="c" column="cId" not-null="true" cascade="delete-orphan"/>
    </class>
    <class name="scratchpad.hibernate.C">
        <id name="id">
            <generator class="increment"/>
        </id>
    </class>
</hibernate-mapping>

Gail Badner
SourceLabs - http://www.sourcelabs.com
Dependable Open Source Systems

> Cascading Delete In Wrong Order
> -------------------------------
>
>          Key: HHH-2421
>          URL: http://opensource.atlassian.com/projects/hibernate/browse/HHH-2421
>      Project: Hibernate3
>         Type: Bug

>   Components: core
>     Versions: 3.2.1
>  Environment: Hibernate 3.2.1, Java5, MySQL 5 (InnoDB)
>     Reporter: CannonBall
>     Priority: Trivial

>
>
> Mapping Document:
> <hibernate-mapping>
>     <class name="scratchpad.hibernate.A">
>         <id name="id">
>             <generator class="increment"/>
>         </id>
>         <list name="bs" cascade="all,delete-orphan">
>             <key column="bId"/>
>             <list-index column="idx"/>
>             <one-to-many class="scratchpad.hibernate.B"/>
>         </list>
>     </class>
>     <class name="scratchpad.hibernate.B">
>         <id name="id">
>             <generator class="increment"/>
>         </id>
>         <many-to-one name="a" column="aId" insert="false" update="false"/>
>         <many-to-one name="c" column="cId" not-null="false"/>
>     </class>
>     <class name="scratchpad.hibernate.C">
>         <id name="id">
>             <generator class="increment"/>
>         </id>
>     </class>
> </hibernate-mapping>
> Code between sessionFactory.openSession() and session.close():
>         long id;
>         SessionFactory factory = new Configuration().configure()
>                 .buildSessionFactory();
>         try {
>             Session s = factory.openSession();
>             try {
>                 Transaction tx = s.beginTransaction();
>                 try {
>                     C c = new C();
>                     s.save(c);
>                     B b = new B();
>                     b.setC(c);
>                     A a = new A();
>                     a.getBs().add(b);
>                     s.save(a);
>                     tx.commit();
>                     id = b.getId();
>                 } catch (Exception e) {
>                     try {
>                         tx.rollback();
>                     } catch (Exception e2) {
>                         // do nothing
>                     }
>                     throw e;
>                 }
>             } finally {
>                 s.close();
>             }
>             s = factory.openSession();
>             try {
>                 Transaction tx = s.beginTransaction();
>                 try {
>                     A a = (A) s.load(A.class, id);
>                     B b = a.getBs().get(0);
>                     a.getBs().remove(b);
>                     s.delete(b.getC());
>                     tx.commit();
>                 } catch (Exception e) {
>                     try {
>                         tx.rollback();
>                     } catch (Exception e2) {
>                         // do nothing
>                     }
>                     throw e;
>                 }
>             } finally {
>                 s.close();
>             }
>         } finally {
>             factory.close();
>         } 
> The generated SQL (show_sql=true):
> Hibernate: select max(id) from C
> Hibernate: select max(id) from A
> Hibernate: select max(id) from B
> Hibernate: insert into C (id) values (?)
> Hibernate: insert into A (id) values (?)
> Hibernate: insert into B (cId, id) values (?, ?)
> Hibernate: update B set bId=?, idx=? where id=?
> Hibernate: select a0_.id as id0_0_ from A a0_ where a0_.id=?
> Hibernate: select bs0_.bId as bId1_, bs0_.id as id1_, bs0_.idx as idx1_, bs0_.id as id1_0_, bs0_.aId as aId1_0_, bs0_.cId as cId1_0_ from B bs0_ where bs0_.bId=?
> Hibernate: select c0_.id as id2_0_ from C c0_ where c0_.id=?
> Hibernate: update B set cId=? where id=?
> Hibernate: update B set bId=null, idx=null where bId=?
> Hibernate: delete from C where id=?
> Hibernate: delete from B where id=? 
> When you have a collection that is mapped with a cascade of 'delete-orphan', when removing an entity from the collection, the corresponding orphan delete is scheduled at the end of the session's deletions queue.  As you can see from my example above, when you have a relationship of A has a list of B's, B has a relationship with C, removing B from the A's list results in its deletion after C's deletion (despite the order of statements dictating C's deletion after B's). If I were to make B's relationship to C not-null, the above code would result in a FK constraint error as C would be removed before B.
> You could force the correct removal of B before C with a manual delete of B like so:
>   A a = (A) s.load(A.class, id);
>   B b = a.getBs().get(0);
>   C c = b.getC();
>   a.getBs().remove(b);
>   s.delete(b);
>   s.delete(c); 

-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators:
   http://opensource.atlassian.com/projects/hibernate/secure/Administrators.jspa
-
For more information on JIRA, see:
   http://www.atlassian.com/software/jira




More information about the hibernate-issues mailing list