单向和双向JPA和Hibernate关联有什么区别?

200

单向关联和双向关联有什么区别?

由于在数据库中生成的表都是相同的,因此我发现唯一的区别是双向关联的每一侧都将引用另一侧,而单向关联则不需要。

这是一个单向关联

public class User {
    private int     id;
    private String  name;
    @ManyToOne
    @JoinColumn(
            name = "groupId")
    private Group   group;
}

public class Group {
    private int     id;
    private String  name;
}

双向关联

public class User {
    private int     id;
    private String  name;
    @ManyToOne
    @JoinColumn(
            name = "groupId")
    private Group   group;
}
public class Group {
    private int         id;
    private String      name;
    @OneToMany(mappedBy="group")
    private List<User>  users;
}

区别在于组是否持有用户的引用。

那么我想知道这是唯一的区别吗?哪个更推荐?


12
该组现在将知道包含哪些用户。我认为这绝非小差异。 - Satadru Biswas
9
当涉及到更新时,双向关系对我来说变得非常混乱。 :) - diyoda_
2
这可能会有所帮助:https://thorben-janssen.com/ultimate-guide-association-mappings-jpa-hibernate/ - L.Zak
4个回答

208
主要区别在于双向关联提供了两个方向的导航访问,因此您可以在没有显式查询的情况下访问另一侧。此外,它允许您对两个方向应用级联选项。
请注意,导航访问并不总是好的,特别是对于"一对很多"和"很多对很多"的关系。想象一个包含数千个用户的组:
- 您将如何访问它们?有这么多用户,通常需要应用一些过滤和/或分页,因此您仍需要执行查询(除非使用collection filtering,但我认为这看起来像一个hack)。在这种情况下,一些开发人员可能倾向于在内存中应用过滤,这显然对性能不利。请注意,拥有这样的关系可能会鼓励这种类型的开发人员在不考虑性能影响的情况下使用它。 - 您将如何向组添加新用户?幸运的是,Hibernate在持久化时查看关系的拥有方,因此您只需设置User.group即可。但是,如果您想保持对象在内存中的一致性,还需要将User添加到Group.users中。但这将使Hibernate从数据库中获取Group.users的所有元素!
所以,我不能同意最佳实践中的建议。你需要仔细设计双向关系,考虑使用情况(你是否需要在两个方向上进行导航访问?)和可能的性能影响。
另请参阅:
- JPA模型中的“ToMany”关系防止 - Hibernate映射集合的性能问题

嗨,谢谢。看起来你是一个Hibernate专家,因为你回答了我的问题:http://stackoverflow.com/questions/5350770/mapping-a-entity-which-have-two-refer-to-another-same-entity 。现在我不确定关系的保持方式,因为我不能在评论中写更多内容,所以我在这里发布了它 http://dpaste.de/J85m/ 。如果可能,请检查一下 :) - hguser
@hguser:如果你最终决定使你的关系双向化,我认为最好在addUser()中调用setGroup(),以保持双方一致。 - axtavt
如果我在group.addUser()中不调用setGroup()会怎样? - hguser
@hguser:关系不会被持久化,参见答案中的第2点。您可以在没有调用addUser()的情况下调用setGroup(),但这将导致内存中的对象状态不一致。 - axtavt
我发现我无法正确映射,你能抽出一些时间在我的Github项目中检查吗?这是一个小项目。 - hguser
显示剩余2条评论

65

这里有两个主要的区别。

访问关联端口

第一个与您将如何访问关系有关。对于单向关联,您只能从一端导航到关联。

因此,对于单向@ManyToOne关联,这意味着您只能从包含外键的子级端口访问关系。

如果您有一个单向@OneToMany关联,则表示您只能从管理外键的父级端口访问关系。

对于双向@OneToMany关联,您可以从父级或子级别两种方式导航关联。

您还需要使用添加/删除实用程序方法来处理双向关联以确保两个端口同步

性能

第二个方面与性能有关。

  1. 对于@OneToMany单向关联与双向关联相比性能不佳
  2. 对于@OneToOne如果Hibernate无法确定应该分配代理还是空值,则双向关联将导致立即提取父级
  3. 对于@ManyToMany集合类型会产生很大的差异,因为SetsLists表现更好

但据我所知,父级始终是具有外键的一方。这两个句子对我来说似乎是矛盾的。从子级别开始,外键所在的地方。从父级别开始,外键所在的地方。 - TOMAS
3
FK(外键)始终在子表中。单向的一对多关系管理可以在子表或联接表中存在的FK。请查看链接以获取更多详细信息。 - Vlad Mihalcea
你好 @Vlad,有一个问题:多对多关联可以是单向的吗?我的意思是性能方面。@ManyToMany @JoinTable(name = "role_privileges", joinColumns = { @JoinColumn(name = "role_id", referencedColumnName = "id") }, inverseJoinColumns = { @JoinColumn(name = "privilege_id", referencedColumnName = "id") }) private Set<Privilege> privileges=new HashSet<Privilege>(); - Alex
3
对于多对多关系,从性能角度来看,它们是单向的还是双向的并不重要。 - Vlad Mihalcea

13

我不确定这是唯一的区别,但这是主要区别。 Hibernate文档也建议使用双向关联:

http://docs.jboss.org/hibernate/core/3.3/reference/en/html/best-practices.html

具体来说:

优先选择双向关联: 单向关联在查询时更难处理。在一个大型应用中,几乎所有的关联 必须在查询中可以双向导航。

个人而言,我对这个笼统的建议略有不满 -- 在我看来,存在一些情况,子项没有任何实际原因要知道其父项(例如,为什么订单项需要知道与之关联的订单?),但我也认为有时候遵循这种双向关联的方式具备价值。由于双向关联不会真正产生太大影响,所以我不觉得遵循它会有什么问题。


1
双向性在某些情况下可能会带来问题,正如axtavt所解释的那样!我会非常小心地处理Hibernate实体中映射的每个关系!除非你确切知道自己在做什么,否则你可能需要无限长的时间来加载Hibernate中的内容。仔细考虑使用情况,并为不同的使用情况使用不同的模型类。例如,在事物列表中,我不需要所有相关对象,只需要一个标签和ID。因此,对于我来说,列表实体与详细实体是不同的(并且非常简单)。 - cslotty

13

在编码方面,双向关系的实现更加复杂,因为根据JPA规范5(第42页),应用程序负责保持两端同步。不幸的是,规范中给出的示例没有提供更多细节,因此无法了解其复杂度。

当不使用二级缓存时,通常没有正确实现关系方法不会成为问题,因为实例在事务结束时被丢弃。

当使用二级缓存时,如果由于错误实现的关系处理方法导致任何内容损坏,则意味着其他事务也将看到损坏的元素(二级缓存是全局共享的)。

一个正确实现的双向关系可以使查询和代码更简单,但如果从业务逻辑上看不合理,则不应使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接