Hibernate Books

All times are UTC - 5 hours [ DST ]



Post new topic Reply to topic  [ 8 posts ] 
Author Message
 Post subject: Removing entity from a @ManyToMany bidirectional association
PostPosted: Tue Jan 17, 2017 8:30 pm 
Newbie

Joined: Mon Dec 12, 2016 3:45 pm
Posts: 16
I am able to successfully add the an association between Parent and Child, however I can't seem to be able to remove it.

Project.class
Code:
public class Project {
    // other properties

    @JsonIgnore
    @ManyToMany(mappedBy = "projectsMemberOf",
            cascade = {CascadeType.PERSIST, CascadeType.MERGE},
            fetch = FetchType.LAZY
    )
    public List<User> getProjectMembers() {
        return projectMembers;
    }

    public void setProjectMembers(List<User> projectMembers) {
        this.projectMembers = projectMembers;
    }
}


User.class
Code:
public class User {
    // other properties
    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    @JoinTable(name = "userProjects",
            joinColumns = @JoinColumn(name = "userId", referencedColumnName = "userId"),
            inverseJoinColumns = @JoinColumn(name = "projectId", referencedColumnName = "projectId"),
            foreignKey = @ForeignKey(name = "FK_userProjects_userId"),
            inverseForeignKey = @ForeignKey(name = "FK_userProjects_projectId")
    )
    public List<Project> getProjectsMemberOf() {
        return projectsMemberOf;
    }

    private void setProjectsMemberOf(List<Project> projectsMemberOf) {
        this.projectsMemberOf = projectsMemberOf;
    }   
}


can save using:
Code:
        project.getProjectMembers().add(user);
        user.getProjectsMemberOf().add(project);
        em.merge(project);


however the following fails to remove the association:
Code:
        project.getProjectMembers().remove(user);
        user.getProjectsMemberOf().remove(project);
        em.merge(project);


Top
 Profile  
 
 Post subject: Re: Removing ManyToMany BiDirectional association
PostPosted: Wed Jan 18, 2017 4:00 am 
Hibernate Team
Hibernate Team

Joined: Thu Sep 11, 2014 2:50 am
Posts: 1385
I see that you have properly synchronized both sides of the association, which is a good start. However, as explained in my book, High-Performance Java Persistence, you need to implement equals and hashCode properly as well.

In your example, I expect that you rely on the default Object reference-based equals and hashCode, which is wrong. Check out this article if you want to use the entity identifier or this article if you want to use a natural key.

There's another thing that's not proper as well. I see you are calling merge after each operation, and I assume you are using it as a save method. This is not the case. Merge is just for detached entities. Check out this article for more details.

_________________
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: Removing entity from a @ManyToMany bidirectional association
PostPosted: Wed Jan 18, 2017 10:05 am 
Newbie

Joined: Mon Dec 12, 2016 3:45 pm
Posts: 16
So reading those articles, I updated the hashCode and equals of both entities and it is still not working.

User
Code:
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof User)) return false;

        User that = (User) o;
        return getUsername().equals(that.getUsername());
    }

    @Override
    public int hashCode() {
        return getUsername().hashCode();
    }


Project
Code:
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Project)) return false;

        Project project = (Project) o;

        return getProjectId() != 0 && getProjectId() == project.getProjectId();
    }

    @Override
    public int hashCode() {
        return 31;
    }


I'm calling merge, because the entites are detached. I fetch them in the service layer, do some work with the detached entities and then pass them back to the service layer to be persisted. Could that be the reason for the association not being removed?


Top
 Profile  
 
 Post subject: Re: Removing entity from a @ManyToMany bidirectional association
PostPosted: Wed Jan 18, 2017 12:01 pm 
Hibernate Team
Hibernate Team

Joined: Thu Sep 11, 2014 2:50 am
Posts: 1385
If you can simulate this in a replicating test case, then you should open a bug.

_________________
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: Removing entity from a @ManyToMany bidirectional association
PostPosted: Wed Jan 18, 2017 1:52 pm 
Newbie

Joined: Mon Dec 12, 2016 3:45 pm
Posts: 16
okay, I'll try and reproduce the bug and file an issue.

I was able to get it to remove the association by calling
Code:
em.merge(user)


Why would I be able to add the association by calling merge on the Project or User, but only be able to remove the association by calling merge on the User? Shouldn't a bi-directional relationship work either way?


Last edited by rodney757 on Wed Jan 18, 2017 2:00 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject: Re: Removing entity from a @ManyToMany bidirectional association
PostPosted: Wed Jan 18, 2017 1:57 pm 
Hibernate Team
Hibernate Team

Joined: Thu Sep 11, 2014 2:50 am
Posts: 1385
That's because the Project is the mappedBy side while the User is the one whose associations are taken into consideration.

_________________
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: Removing entity from a @ManyToMany bidirectional association
PostPosted: Wed Jan 18, 2017 2:23 pm 
Newbie

Joined: Mon Dec 12, 2016 3:45 pm
Posts: 16
I had a typo in my post.

I can add the association from either side, but only remove from the User.

I thought that with bidirectional manyTomany both sides "owned" the relationship?


Top
 Profile  
 
 Post subject: Re: Removing entity from a @ManyToMany bidirectional association
PostPosted: Wed Jan 18, 2017 2:52 pm 
Hibernate Team
Hibernate Team

Joined: Thu Sep 11, 2014 2:50 am
Posts: 1385
In every bidirectional association, only the non-mappedBy side own the association. You synchronize both ends to avoid other issues.

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


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 8 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.