Bug 221 : Delete on bean with ManyToMany ... and no casade.REMOVE should not delete from intersection table
Priority 
High
Reported Version 
 
Logged By 
Rob
Status 
Fixed
Fixed Version 
2.4.0
Assigned To 
 
Product 
Ebean - core
Duplicate Of 
 
Created 
22/02/2010
Updated 
22/02/2010
Type 
Bug
 
Attachments 
No attachments

Hello,

I have Users, Roles and a many-to-many between them (userroles table).

Users have validRoles and Roles have validUsers. Neither has
CascadeType specified. To my surprise, when I delete a Role, it
deletes from userroles as well. The database has cascade "no action"
on userroles.roleid and userroles.userid.

Why are records in userroles being deleted?

Thanks.

/Daryl

 
Rob 22 Feb 09:18
rob comments...

Yes, sorry I was not clear.

I believe we should change this behavior (and treat this as a bug).

For we did that I wanted to check the JPA 2 spec to see if there was anything we missed wrt the behavior with and without the cascade.REMOVE. It appears nothing around this area has changed in JPA2. The important things to note for users... is that they are going to get different behavior around ManyToMany deletes (with and without cascade.REMOVE) with different vendors which is a shame (and potentially a bit dangerous when you swap vendors).


Behaviour without cascade.REMOVE
-----------------------------------------------------
The current behavior does delete the intersection rows whether you like it or not.

As Daryl has pointed out - this means we can't handle the cases where you want the delete to not cascade but instead to fail when there are rows in the intersection table (sol).

In changing the behavior we also need to supply a new method deleteManyToManyAssociations(...) so that users can easily explicitly do the deletion from the intersection table if that is the behavior they need. This also is nice as it compliments our saveManyToManyAssociations() method. NB: You can also use cascade REMOVE (or cascade ALL) but then you are relying on vendor (Ebean) specific behavior (and you may not like that or at least need to note it).


Behavior with cascade.REMOVE (should not change)
---------------------------------------------------------------------------------
Users should note that with cascade.REMOVE or cascade.ALL on a ManyToMany ... they are going to get different behavior from different vendors. I think Ebean should follow its current behavior which effectively means the intersection rows get deleted but the other associated beans DO NOT get deleted.

I believe this makes sense (not to casade the delete across to the associated beans) as ManyToMany typically represents an ASSIGNMENT between two entities with independent life cycles. If users want to delete the beans on the other side of the ManyToMany they can do this easily anyway with Ebean (delete can take an iterator).

(We need to justify our behavior as its up to the vendor what they do here)

So hopefully that is a bit clearer ... all comments welcome... and if we don't get any arguments against we will log a bug and change the behaviour (to not delete from the intersection automatically) and add the deleteManyToManyAssociations(...) method.

... and thanks to Daryl for highlighting this issue !!!


Cheers, Rob.

Rob 22 Feb 09:19
Daryl vote

> Behavior with cascade.REMOVE (should not change)
> --------------------------------------------------------------------------- ------
> Users should note that with cascade.REMOVE or cascade.ALL on a ManyToMany
> ... they are going to get different behavior from different vendors. I think
> Ebean should follow its current behavior which effectively means the
> intersection rows get deleted but the other associated beans *DO NOT* get
> deleted.

+1 for me as I agree.

/Daryl

Rob 22 Feb 09:28
Fixed in HEAD

Fixed in HEAD ... added

/**
     * Delete the associations (from the intersection table) of a ManyToMany
     * given the owner bean and the propertyName of the ManyToMany collection.
     * <p>
     * This returns the number of associations deleted.
     * </p>
     */
    public int deleteManyToManyAssociations(Object ownerBean, String propertyName) {

woResponse

Upload a file