9

我有一个 Group 实体,它有一个多对多关系中的用户实体列表。它由包含两个 ID 的典型连接表映射。这个列表可能非常大,一个组中有一百万或更多的用户。

我需要向组中添加一个新用户,通常类似于

group.getUsers().add(user);
user.getGroups().add(group);

em.merge(group);
em.merge(user);

如果我了解典型的 JPA 操作,这是否需要将 100 万以上用户的整个列表拉到集合中才能添加新用户然后保存?对我来说,这听起来不太可扩展。

我不应该在 JPA 中定义这种关系吗?在这种情况下,我应该直接操作连接表条目吗?

请原谅松散的语法,我实际上使用的是 Spring Data JPA,所以我不经常直接直接使用实体管理器,但这个问题似乎对 JPA 来说很普遍,所以我想这样提出。

4

3 回答 3

4

像这样设计您的模型并使用 UserGroup 进行关联。

@Entity
public class User {

   @OneToMany(cascade = CascadeType.ALL, mappedBy = "user",fetch = FetchType.LAZY)
    @OnDelete(action = OnDeleteAction.CASCADE)
    private Set<UserGroup> userGroups = new HashSet<UserGroup>();

}


@Entity
@Table(name="user_group", 
    uniqueConstraints = {@UniqueConstraint(columnNames = {"user_id", "group_id"})})
public class UserGroup {


    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id", nullable = false)
    @ForeignKey(name = "usergroup_user_fkey")
    private User user;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "group_id", nullable = false)
    @ForeignKey(name = "usergroup_group_fkey")  
    private Group group;

}


@Entity
public class Group {

    @OneToMany(cascade = CascadeType.ALL, mappedBy="group", fetch = FetchType.LAZY )
    @OnDelete(action = OnDeleteAction.CASCADE)      
    private Set<UserGroup>  userGroups  = new HashSet<UserGroup>();

}

这样做。

     User user =     findUserId(id); //All groups wont be loaded they are marked lazy
     Group group =  findGroupId(id);  //All users wont be loaded they are marked lazy

     UserGroup  userGroup = new UserGroup();
     userGroup.setUser(user);
     userGroup.setGroup(group);

     em.save(userGroup);
于 2013-09-20T05:34:34.247 回答
1

有效地使用 ManyToMany 映射会将集合缓存在实体中,因此您可能不希望对大型集合执行此操作,因为显示它或在触发时传递实体会降低性能。

相反,您可以删除双方的映射,并为关系表创建一个实体,当您确实需要访问关系时,您可以在查询中使用该实体。使用中间实体将允许您使用分页和游标,以便您可以限制可能被带回可用块的数据,并且您可以插入新实体以轻松表示新关系。

EclipseLink 的属性更改跟踪确实允许添加到集合而不需要触发关系,以及其他性能增强。这是通过编织启用的,并且可用于不维护顺序的集合类型。

于 2013-09-20T14:55:00.853 回答
0

由返回的集合类getUsers()并且getGroups()不必将其内容驻留在内存中,并且如果您打开了延迟获取,就像我假设您为如此大的关系所做的那样,持久性提供程序应该足够聪明以识别您不是试图读取内容,而是添加一个值。(类似地,调用size()集合通常会导致 SQLCOUNT查询,而不是实际加载和计算元素。)

于 2013-09-20T05:20:02.243 回答