Search code examples
javaspringhibernatemany-to-manybidirectional-relation

Many to many relationship for same type entity


I have an entity as below. I am curious if it is possible to create a relationship as I will be describing with the example:

  • I am creating 2 Person entities Michael and Julia.
  • I am adding Julia to Michael's friends set.
  • After that I am retrieving Michael as a JSON response and Julia is available in the response. But when I am retrieving Julia, her friends set is empty. I want to create the bidirectional friendship relation by saving just one side of the friendship. I would like to get Michael on Julia's friends set without doing any other operations. I think that it must be managed by Hibernate. Is it possible and how should I do it?

    @ToString(exclude = "friends") // EDIT: these 2 exclusion necessary
    @EqualsAndHashCode(exclude = "friends")
    public class Person{
    
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long id;
    
    @Column(name = "name",unique = true)
    private String name;
    
    @JsonIgnoreProperties("friends") // EDIT: will prevent the infinite recursion
    @ManyToMany(cascade = CascadeType.ALL)
    @JoinTable(name = "FRIENDSHIP",
           joinColumns = @JoinColumn(name = "person_id", 
    referencedColumnName = "id"),
           inverseJoinColumns = @JoinColumn(name = "friend_id", 
    referencedColumnName = "id"))
    private Set<Person> friends;
    

Here is my service layer code for creating a friendship:

    @Override
    public Person addFriend(String personName, String friendName) 
        throws FriendshipExistsException, PersonNotFoundException {
    Person person = retrieveWithName(personName);
    Person friend = retrieveWithName(friendName);
    if(!person.getFriends().contains(friend)){
        person.getFriends().add(friend);
        return repository.save(person);
    }
    else{
        throw new FriendshipExistsException(personName, friendName);
    }
}

Related Question: N+1 query on bidirectional many to many for same entity type

Updated the source code and this version is working properly.


Solution

  • // Creating a graph to help hibernate to create a query with outer join.
    @NamedEntityGraph(name="graph.Person.friends",
        attributeNodes = @NamedAttributeNode(value = "friends"))
    class Person {}
    
    interface PersonRepository extends JpaRepository<Person, Long> {
        // using the named graph, it will fetch all friends in same query
        @Override
        @EntityGraph(value="graph.Person.friends")
        Person findOne(Long id);
    }
    
    @Override
    public Person addFriend(String personName, String friendName) 
        throws FriendshipExistsException, PersonNotFoundException {
        Person person = retrieveWithName(personName);
        Person friend = retrieveWithName(friendName);
        if(!person.getFriends().contains(friend)){
            person.getFriends().add(friend);
            friend.getFriends().add(person); // need to setup the relation
            return repository.save(person); // only one save method is used, it saves friends with cascade
        } else {
            throw new FriendshipExistsException(personName, friendName);
        }
    

    }

    If you check your hibernate logs, you will see:
    Hibernate: insert into person (name, id) values (?, ?)
    Hibernate: insert into person (name, id) values (?, ?)
    Hibernate: insert into friendship (person_id, friend_id) values (?, ?)
    Hibernate: insert into friendship (person_id, friend_id) values (?, ?)