欢迎访问 生活随笔!

生活随笔

当前位置: 首页 >

Hibernate事实:有利于双向集vs列表

发布时间:2023/12/3 53 豆豆
生活随笔 收集整理的这篇文章主要介绍了 Hibernate事实:有利于双向集vs列表 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

Hibernate是一个很棒的ORM工具,它极大地简化了开发,但是如果您想正确地使用它,则有很多陷阱。

在大中型项目中,双向父子关联非常常见,这使我们能够浏览给定关系的两端。

在控制关联的持久/合并部分时,有两个可用选项。 其中将有负责同步收集变化的@OneToMany结束,但是这是一个低效率的做法,这是很好的描述在这里 。

最常见的方法是@ManyToOne端控制关联并且@OneToMany端使用“ mappedBy”选项时。

我将讨论后一种方法,因为就执行的查询数量而言,这是最常见,最有效的方法。

因此,对于双向集合,我们可以使用java.util.List或java.util.Set。

根据Hibernate docs的说法,列表和文件包比集合更有效。

但是当我看到以下代码时,我仍然感到焦虑:

@Entity public class Parent {...@OneToMany(cascade = CascadeType.ALL, mappedBy = "parent", orphanRemoval = true) private List children = new ArrayList()public List getChildren() { return children; }public void addChild(Child child) { children.add(child); child.setParent(this); }public void removeChild(Child child) { children.remove(child); child.setParent(null); } }@Entity public class Child {...@ManyToOne private Parent parent;public Parent getParent() { return parent; }public void setParent(Parent parent) { this.parent = parent; } }Parent parent = loadParent(parentId); Child child1 = new Child(); child1.setName("child1"); Child child2 = new Child(); child2.setName("child2"); parent.addChild(child1); parent.addChild(child2); entityManager.merge(parent);

这是因为在最近五年中,当在父关联上调用合并操作时,我一直在插入重复的子代。 发生这种情况是由于以下问题: HHH-3332和HHH-5855 。

我最近一直在测试一些Hibernate版本,并且仍然在3.5.6、3.6.10和4.2.6版本上进行复制。 因此,经过5年在许多项目上看到这一点后,您了解了为什么我对使用列表与集合持怀疑态度。

这是在运行复制此问题的测试用例时得到的结果,因此添加两个子级,我们得到:

select parent0_.id as id1_2_0_ from Parent parent0_ where parent0_.id=? insert into Child (id, name, parent_id) values (default, ?, ?) insert into Child (id, name, parent_id) values (default, ?, ?) insert into Child (id, name, parent_id) values (default, ?, ?) insert into Child (id, name, parent_id) values (default, ?, ?)

仅当合并操作从父级到子级联时,才会出现此问题,并且存在以下变通办法:

  • 合并孩子而不是父母
  • 在合并父母之前先让孩子坚持
  • 从父级删除Cascade.ALL或Cascade.MERGE,因为它只影响合并操作,而不影响持久化操作。

但是所有这些都是黑客,在大型项目中很难遵循,因为许多开发人员都在相同的代码库上工作。

因此,我的首选方式是使用Set,即使有时它们的效率不如Lists更好,但是由于我一直偏爱正确性与性能优化,因此最好使用Set。

当涉及到这类问题时,最好具有代码约定,因为它们易于添加到项目开发指南中,并且易于记忆和采用。

使用集合的一个优点是,它迫使您定义适当的equals / hashCode策略(该策略应始终包括实体的业务密钥。业务密钥是一种字段组合,该字段组合在父级的子级中是唯一的或唯一的,并且甚至在之前也是一致的)以及将实体持久保存到数据库中之后)。

如果您担心会失去以添加孩子的相同顺序保存孩子的“列表”功能,那么您仍然可以为Sets模仿。

默认情况下,集合是无序的和未排序的,但是即使您不能对它们进行排序,也可以通过使用@OrderBy JPA注释按给定的列对它们进行排序,如下所示:

@Entity public class LinkedParent {...@OneToMany(cascade = CascadeType.ALL, mappedBy = "parent", orphanRemoval = true) @OrderBy("id") private Set children = new LinkedHashSet();...public Set getChildren() { return children; }public void addChild(LinkedChild child) { children.add(child); child.setParent(this); }public void removeChild(LinkedChild child) { children.remove(child); child.setParent(null); } }

加载父级的子级时,生成SQL类似于:

select children0_.parent_id as parent_i3_3_1_, children0_.id as id1_2_1_, children0_.id as id1_2_0_, children0_.name as name2_2_0_, children0_.parent_id as parent_i3_2_0_ from LinkedChild children0_ where children0_.parent_id=? order by children0_.id

结论:

如果您的领域模型要求使用列表而不是集合,则将打破您的约束,不允许重复。 但是,如果您需要重复项,则仍然可以使用索引列表。 据说Bag是未排序且“无序的”(即使它按照在数据库表中添加子的顺序来检索子)。 因此,索引列表也将是一个不错的选择,对吗?

我还想提请注意一个5年的bug,它影响了多个Hibernate版本,并且是我在多个项目中复制的一个版本。 当然,有一些解决方法,例如删除Cascade.Merge或合并Children vs the Parent,但是有许多开发人员不知道此问题及其解决方法。

根据Hibernate docs:集是“ 表示多值关联的推荐方法 ”,而且我已经看到很多情况下使用Bags作为默认双向集合,即使无论如何集都是更好的选择。

因此,我仍然对Bags保持谨慎,如果我的领域模型强加使用List,我总是会选择索引的。

  • 代码可在GitHub上获得 。

参考: Hibernate Facts:来自Vlad Mihalcea博客博客的JCG合作伙伴 Vlad Mihalcea偏爱双向集合与列表 。

翻译自: https://www.javacodegeeks.com/2013/10/hibernate-facts-favoring-bidirectional-sets-vs-lists.html

总结

以上是生活随笔为你收集整理的Hibernate事实:有利于双向集vs列表的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。