Hibernate Books

All times are UTC - 5 hours [ DST ]



Post new topic Reply to topic  [ 3 posts ] 
Author Message
 Post subject: Hibernate @ManyToMany overzealous one-shot delete
PostPosted: Fri Dec 16, 2016 3:01 pm 
Newbie

Joined: Fri Dec 16, 2016 1:51 pm
Posts: 2
Hello all. I have looked through the forums and so far have not been able to find a similar situation so hopefully this is not a duplicate. This is also my first post so I apologize in advance for the format. The machine that contains the code does not have access to the internet so the below code is a brief snippet of what is relevant. if I missed something of relevance please let me know :)

I have the following table structure which cannot be changed in our database:

Code:
                                   
                                    ------------------- Child_A
                                    |                   child_id                   
                                    |                       
                                    |               
Parent     --------------- Parent_Child ---------- Child_B
parent_id                  parent_id               child_id
                           child_id             
                                    |                   
                                    |                         
                                    ------------------ Child_C
                                                       child_id

The jist of it is that the parent table uses the same join table for its association with several different child tables. The Parent table is mapped as follows (The ManyToMany is because there is no unique constraint for either column in the join table):

Code:
@Entity
@Table(name = "Parent")
public class Parent implements Serializable
{
    @ManyToMany(cascade = {CascadeType.All})
    @JoinTable(name = "Parent_Child",
        joinColumns = {@JoinColumn(name = "parent_id", referencedColumname = "parent_id")},
        inverseJoinColumns = {@JoinColumn(name = "child_id", referencedColumnName = "child_id")})
    private Set<ChildA> childrenA;

    @ManyToMany(cascade = {CascadeType.All})
    @JoinTable(name = "Parent_Child",
        joinColumns = {@JoinColumn(name = "parent_id", referencedColumname = "parent_id")},
        inverseJoinColumns = {@JoinColumn(name = "child_id", referencedColumnName = "child_id")})
    private Set<ChildB> childrenB;

    ...
}


Each of the children are mapped as follows:
Code:
@Entity
@Table(name = "Child_A")
public class ChildA implements Serializable
{
    @ManyToMany(mappedBy = "childrenA")
    private Set<Parent> parents;

    ...
}

@Entity
@Table(name = "Child_B")
public class ChildB implements Serializable
{
    @ManyToMany(mappedBy = "childrenB")
    private Set<Parent> parents;

    ...
}


Now assume the following code:

Code:
...

Parent parent = new Parent();
parent.setId(id1);

ChildA childA1 = new ChildA();
childA1.setId(id2);

ChildB childB1 = new ChildB();
childB1.setId(id3);

parent.getChildrenA().add(childA1);
parent.getChildrenB().add(childB1);
session.save(parent);


At this point, everything is fine. I see the inserts into Parent_Child for both relationships. However the problem appears when performing a delete:

Code:
...

parent.getChildrenA().clear();
session.update(parent);


At this point, the generated SQL performs a one-shot delete and actually removes the relationships to both child A and child B. From what I read, hibernate will optimize when it detects an empty collection so instead of

Code:
delete from Parent_Child where parent_id = ? and child_id = ?


I am seeing

Code:
delete from Parent_Child where parent_id = ?


Is there any way around this? Any help on the matter is greatly appreciated.

Thanks,
Matthew


Top
 Profile  
 
 Post subject: Re: Overzealous one-shot delete
PostPosted: Mon Jan 09, 2017 6:00 am 
Hibernate Team
Hibernate Team

Joined: Thu Sep 11, 2014 2:50 am
Posts: 1291
I think the problem is that you are using the same join table: Parent_Child. That would have been appropriate if Child_A and Child_B would inherit from a common base entity type, in which case you'd have required only one @ManyToMany association.

In your case, you need two separate join tables:

- Parent_Child_A
- Parent_Child_B

and the mappings become:

Code:
@ManyToMany(cascade = {CascadeType.All})
@JoinTable(name = "Parent_Child_A",
    joinColumns = {@JoinColumn(name = "parent_id", referencedColumname = "parent_id")},
    inverseJoinColumns = {@JoinColumn(name = "child_id", referencedColumnName = "child_id")})
private Set<ChildA> childrenA;

@ManyToMany(cascade = {CascadeType.All})
@JoinTable(name = "Parent_Child_B",
    joinColumns = {@JoinColumn(name = "parent_id", referencedColumname = "parent_id")},
    inverseJoinColumns = {@JoinColumn(name = "child_id", referencedColumnName = "child_id")})
private Set<ChildB> childrenB;

_________________
If you liked my answer, you are going to love my High-Performance Java Persistence book and my blog as well.


Top
 Profile  
 
 Post subject: Re: Hibernate @ManyToMany overzealous one-shot delete
PostPosted: Mon Jan 09, 2017 12:43 pm 
Newbie

Joined: Fri Dec 16, 2016 1:51 pm
Posts: 2
Thank you for your response vlad!

Unfortunately modifying the database is out of my control so that is not an option. I agree that the multi-purpose join table is indeed causing the issue but there simply isn't a way for me to change that.

I did get back to this last week and was able to find a solution that is working in my case. For anyone that is interested:

What I ended up doing was digging through the hibernate code a little till I found where it decided to perform a one-shot delete instead of a row-by-row delete. It checks to ensure the collection is empty and that no filters are applied before performing the one-shot delete.

Since, in my case, it is never appropriate to do a one-shot delete on "Parent_Child", I created a no-cost filter definition with the default condition being "1 = 1". I then applied that filter to each collection joined using the "Parent_Child" table. This resulted in the row-by-row delete occurring even if the collection was cleared out.

Although not the ideal solution, it is the best I have found without making changes to the database.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 3 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.