添加到双向 ManyToMany 集合检索已经相关的所有条目

Posted

技术标签:

【中文标题】添加到双向 ManyToMany 集合检索已经相关的所有条目【英文标题】:Adding to bidirectional ManyToMany collection retrieves all entries already in relation 【发布时间】:2019-02-26 01:22:50 【问题描述】:

我有用户组实体中的用户映射的用户实体

USER.java

@ManyToMany(mappedBy = "users")
private Set<UserGroup> userGroups;

public void addGroup(UserGroup userGroup)
this.userGroups.add(userGroup);
userGroup.addUser(this);

UserGroup.java

@ManyToMany
@JoinTable(name = "group_users", joinColumns = @JoinColumn(name = "group_id"),
        inverseJoinColumns = @JoinColumn(name = "user_id"))
private Set<User> users;

public void addUser(User user) 
   this.users.add(user);

我有一个方法可以在 UserService 中为用户添加组:

@Transactional
public void addGroupsToUser(UserAddGroupsCommand userAddGroupsCommand, String username) 
    User user = userRepository.findByUsername(username);
    if (user != null) 
        List<UserGroup> userGroupList = userGroupRepository.findAllById(userAddGroupsCommand.getId());
        for (UserGroup userGroup : userGroupList) 
            user.addGroup(userGroup);
        
        userRepository.save(user);
    

问题是,当我遍历组并将用户添加到每个组时,出于某种原因,Hibernate 选择了该组中的所有用户(来自连接表)。

Hibernate: 
select
    users0_.group_id as group_id1_3_1_,
    users0_.user_id as user_id2_3_1_,
    user1_.id as id1_7_0_,
    user1_.email as email2_7_0_,
    user1_.firstname as firstnam3_7_0_,
    user1_.is_admin as is_admin4_7_0_,
    user1_.lastname as lastname5_7_0_,
    user1_.password as password6_7_0_,
    user1_.username as username7_7_0_ 
from
    group_users users0_ 
inner join
    user user1_ 
        on users0_.user_id=user1_.id 
where
    users0_.group_id in (
        select
            usergroup0_.id 
        from
            user_group usergroup0_ 
        where
            usergroup0_.id in (
                ? , ?
            )
    )

如何避免这种情况?任何选项都可以避免这种情况而无需编写自己的查询,只需使用注释和 JPA Repository 方法?

【问题讨论】:

【参考方案1】:

添加组时有2个选择:

用于在用户调用user.getUserGroups().add(group)中获取组

select usergroups0_.user_id as user_id2_0_0_, usergroups0_.group_id as group_id1_0_0_, usergroup1_.id as id1_8_1_ 
from group_users usergroups0_ 
inner join UserGroup usergroup1_ on usergroups0_.group_id=usergroup1_.id 
where usergroups0_.user_id=?

用于在组调用中获取用户group.addUser(user)

select users0_.group_id as group_id1_0_0_, users0_.user_id as user_id2_0_0_, user1_.id as id1_7_1_ 
from group_users users0_ 
inner join User user1_ on users0_.user_id=user1_.id 
where users0_.group_id=?

users 字段需要初始化,无论是延迟加载还是急切加载。

【讨论】:

是的,但是为什么 Hibernate 会从我想将该用户添加到的每个组中选择所有用户。我的意思是为什么不直接将 user_id group_id 插入到联接表 group_users 中,而不是从 group_users 中选择与我尝试插入的用户无关的所有条目? 因为您正在访问它。 Hibernate 不知道您想对该集合做什么。这就是它的工作原理。如果需要,您仍然可以对该表执行本机 SQL 插入。如果您想完全控制查询,则不适合使用 JPA。 现在就明白了。但是 Entity Graph 是不是可以解决这个问题? 没有。如果使用 fetch graph,只有实体图指定的属性才会被视为 FetchType.EAGER 谢谢你,彼得。那么您对此有何建议?我应该写查询吗?据我所知,我无法将条目插入连接表。

以上是关于添加到双向 ManyToMany 集合检索已经相关的所有条目的主要内容,如果未能解决你的问题,请参考以下文章

Jpa中ManyToMany和OneToMany的双向控制

休眠中ManyToMany关系的无限递归

hibernate manytomany 双向

Symfony2 2.3.7 -Doctrine 2.4.1:ManyToMany 关系未保存到数据库

如何在 Kotlin 中使用 JPA ManyToMany 双向关系

在 ManyToMany 双向映射中,findAll() 获取行,每行包含其子项,而子项又包含其自身