我可以将 JPA 集合(地图或列表)扩展到多远
How far I can scale a JPA collection (Map or List)
我正在通过 JPA 实现一个由 SQL 数据库支持的在线文件系统。对于目录层次结构,我创建了一个如下所示的实体:
@Entity
@Table(name = "PATH")
public class Path extends BaseObject implements Serializable {
@Column(nullable = false)
private String name;
@ManyToOne
private BaseObject reference;
@OneToMany(fetch = FetchType.LAZY, orphanRemoval = true)
@MapKey(name = "name")
private Map<String, Path> members;
@ManyToOne
private Path parent;
//etc
效果非常好。根节点是 Path 实例,其中 parent 为空。通过这个方案,我可以尽可能轻松地进行树的上下遍历和查找。
问题是这可以扩展到什么程度。例如,我的根 Path 实例之一将为系统的每个用户分配一个成员。几十个甚至几百个用户还好,如果我的网站吸引了数万甚至数十万用户怎么办?
如果必须的话,我可以放弃 members 列并找到具有 SQL SELECT[=24= 的成员节点] 操作,但我讨厌那样做。是否有任何准则来确定此 Map 结构在变得不切实际之前必须达到多大?
members
关联默认是惰性的。所以加载路径时不会发生任何坏事,直到......你开始使用这个 members
字段(即调用它的任何方法)。然后数以千计的 children 将被加载到内存中,这根本无法很好地扩展。但是使用专用查询也不会更好:数以千计的 children 也会被加载到内存中。
问题在于...您应该简单地避免在内存中加载路径的所有 children。您无需执行此操作即可使用现有 parent 创建新路径。您可能必须这样做才能在屏幕上显示一条路径的所有 children,但如果您有数千条路径,那是不现实的,因此您最好使用分页查询按切片显示它们20 或 50。
所以,我确实会放弃这种过于危险的 OneToMany 关联,并使用 ad-hoc 查询来获取您需要的 children 切片。我更关心的是 link 到 parent。由于您已将它们定义为急切加载(这是 toOne 关联的默认设置),因此每次加载路径时,JPA 都会加载其 parent、grandparent 及其 grand-grandparent,等等,直到根。如果你的树很深,那可能会有问题。您最好将关联定义为惰性的(我基本上为所有关联都这样做)。
最后,请注意您的映射不正确。你在这里有一个双向关联,因此 OneToMany members
是 ManyToOne parent
的反面,所以它应该用
注释
@OneToMany(mappedBy = "parent", orphanRemoval = true)
我正在通过 JPA 实现一个由 SQL 数据库支持的在线文件系统。对于目录层次结构,我创建了一个如下所示的实体:
@Entity
@Table(name = "PATH")
public class Path extends BaseObject implements Serializable {
@Column(nullable = false)
private String name;
@ManyToOne
private BaseObject reference;
@OneToMany(fetch = FetchType.LAZY, orphanRemoval = true)
@MapKey(name = "name")
private Map<String, Path> members;
@ManyToOne
private Path parent;
//etc
效果非常好。根节点是 Path 实例,其中 parent 为空。通过这个方案,我可以尽可能轻松地进行树的上下遍历和查找。
问题是这可以扩展到什么程度。例如,我的根 Path 实例之一将为系统的每个用户分配一个成员。几十个甚至几百个用户还好,如果我的网站吸引了数万甚至数十万用户怎么办?
如果必须的话,我可以放弃 members 列并找到具有 SQL SELECT[=24= 的成员节点] 操作,但我讨厌那样做。是否有任何准则来确定此 Map 结构在变得不切实际之前必须达到多大?
members
关联默认是惰性的。所以加载路径时不会发生任何坏事,直到......你开始使用这个 members
字段(即调用它的任何方法)。然后数以千计的 children 将被加载到内存中,这根本无法很好地扩展。但是使用专用查询也不会更好:数以千计的 children 也会被加载到内存中。
问题在于...您应该简单地避免在内存中加载路径的所有 children。您无需执行此操作即可使用现有 parent 创建新路径。您可能必须这样做才能在屏幕上显示一条路径的所有 children,但如果您有数千条路径,那是不现实的,因此您最好使用分页查询按切片显示它们20 或 50。
所以,我确实会放弃这种过于危险的 OneToMany 关联,并使用 ad-hoc 查询来获取您需要的 children 切片。我更关心的是 link 到 parent。由于您已将它们定义为急切加载(这是 toOne 关联的默认设置),因此每次加载路径时,JPA 都会加载其 parent、grandparent 及其 grand-grandparent,等等,直到根。如果你的树很深,那可能会有问题。您最好将关联定义为惰性的(我基本上为所有关联都这样做)。
最后,请注意您的映射不正确。你在这里有一个双向关联,因此 OneToMany members
是 ManyToOne parent
的反面,所以它应该用
@OneToMany(mappedBy = "parent", orphanRemoval = true)