【问题标题】:Many to many relationship for same type entity同一类型实体的多对多关系
【发布时间】:2018-01-31 21:08:41
【问题描述】:

我有一个实体如下。我很好奇是否可以像我将通过示例描述的那样创建关系:

  • 我正在创建 2 个 Person 实体 MichaelJulia
  • 我正在将 Julia 添加到 Michael 的朋友集中。
  • 之后,我将 Michael 作为 JSON 响应检索,并且 Julia 在响应中可用。但是当我检索 Julia 时,她的朋友集是空的。我想通过只保存友谊的一侧来创建双向友谊关系。我想在不做任何其他操作的情况下让迈克尔加入 Julia 的朋友圈。我认为它必须由 Hibernate 管理。有可能吗?我应该怎么做?

    @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;
    

这是我创建友谊的服务层代码:

    @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);
    }
}

相关问题: N+1 query on bidirectional many to many for same entity type

更新了源代码,此版本运行正常。

【问题讨论】:

  • 您需要手动设置双向关系。喜欢friend.addFriend(person) and person.addFriend(friend)。如果要处理此类关系,请考虑使用图形数据库。我更喜欢在我的实体中创建一个辅助方法,例如makeFriendship(Person person),并在那里设置关系以使代码更具可读性。
  • @BogdanOros 我会以这种方式进行更新,但我不确定是否应该通过添加到彼此的朋友列表中手动进行更新。
  • Official documentation 表明它是这样工作的。这样做是为了支持数据库中的关系,因为在关系数据库中,您需要有两行来指定此关系。
  • @BogdanOros 我知道并确切地询问这是否可能。我想通过一次保存操作在友谊表中插入两行。
  • 你会调用save操作那些,因为你已经配置了级联,它会自动持久化更新友谊关系的子集。

标签: java spring hibernate many-to-many bidirectional-relation


【解决方案1】:
// 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);
    }

}

如果您检查您的休眠日志,您将看到:
休眠:插入个人(姓名,ID)值(?,?)
休眠:插入人(姓名,身份证)值(?,?)
Hibernate:插入友谊(person_id,friend_id)值(?,?)
Hibernate:插入友谊(person_id,friend_id)值(?,?)

【讨论】:

  • 正好给出stackoverflow错误就行了:friend.getFriends().add(person);
  • 试过同样的例子,没有stackoverflow错误,你能粘贴stackstrace吗?
  • 我刚刚解决了stackoverflow错误。 Lombok 的 toString()、equals() 和 hashCode() 方法导致了它。我排除了朋友元素而不是解决它。现在我在找人时遇到了问题。例如,当我得到 Julia 时,Michael 在朋友列表中,它正在尝试获取 Michael 的朋友列表,并且它正在将 Julia 带入它,并且它以无限循环的形式继续下去。这是 N+1 查询问题,我正在尝试解决它。​​
  • 是的,Lombok 无法解析向后引用,这就是为什么我使用数据类等迁移到 Kotlin。
  • 你对我上面提到的问题有什么解决建议吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-23
  • 2023-03-07
相关资源
最近更新 更多