是否初始化JPA关系映射?

33

在一对多的 JPA 关联中,将关系初始化为空集合被认为是最佳实践吗?例如:

@Entity
public class Order { 

   @Id
   private Integer id;

   // should the line items be initialized with an empty array list or not?
   @OneToMany(mappedBy="order")
   List<LineItem> lineItems = new ArrayList<>();

}

在上面的例子中,是否最好将lineItems定义为默认值为空的ArrayList?有哪些优缺点?

4个回答

33

JPA本身不关心集合是否已初始化。使用JPA从数据库中检索订单时,JPA将始终返回具有非空订单行列表的订单。

原因:因为订单可以有0、1或N个行,最好用空的、大小为1的或大小为N的集合来表示。如果集合为null,则需要在代码中随处检查它。例如,如果列表为null,这个简单的循环将导致NullPointerException:

for (OrderLine line : order.getLines()) {
    ...
}

因此,最好通过始终具有非空集合来使其成为不变量,即使对于新创建的实体实例也是如此。这使得创建新订单的生产代码更加安全、更加干净。这也使得你的单元测试,使用不来自数据库的Order实例,更加安全、更加干净。


我想知道如果你将一个字段声明为“final”,然后使用延迟加载,会发生什么。 - basin

2
我更喜欢像这样的工具:

public static <T> void forEach(Collection<T> values, Consumer<T> consumer) {
  if (values != null) values.stream().forEach(consumer);
}

并在代码中像这样使用它:
Utils.forEach(entity.getItems(), item -> {
    // deal with item
});

我不同意:循环遍历行只是可以在该列表上执行的众多可能操作之一。按照这种方式,您将不得不为列表上的每个可能操作编写一个实用程序!即使您重用现有的实用程序以避免重新发明轮子,也不能保证其他开发人员在对列表执行操作时会使用该实用程序。如果您不在该列表属性上公开一个setter,或者如果您使用@NonNull注释,您可以确保永远不会抛出NullPointerException - fbastien

2
我的建议是不要初始化它们。
我们遇到了这样一种情况,即我们初始化了集合,然后连续两次检索相同的实体。在第二次检索之后,一个应该有数据的惰性加载集合在调用其getter后为空。另一方面,在第一次检索后调用getter时,集合确实加载了数据。理论上,第二次检索从会话中获取了一个已将其集合初始化为空的托管实体,看起来已经加载或已修改,因此没有进行惰性加载。解决方法是不要初始化集合。这样,我们就可以在事务中多次检索实体,并使其惰性加载的集合正确加载。
还有一件事需要注意:在不同的环境中,行为是不同的。当在同一事务中第二次检索已检索的实体的集合getter时,集合被惰性加载得很好。
不幸的是,我没有关于两个环境之间差异的信息。看起来 - 尽管我们没有100%证明它并且没有确定实现 - 不同的JPA实现在处理初始化集合方面工作方式不同。
我们正在使用Hibernate - 只是不知道我们在两个平台上使用的哪个版本。

2
我建议您使用Guava的不可变集合,例如:
import com.google.common.collect.ImmutableList;
// ...
@OneToMany(mappedBy="order")
List<LineItem> lineItems = ImmutableList.of();

这个习惯用语不会创建新的空列表,而是重复使用一个代表空列表的单个实例(类型无关紧要)。这是函数式编程语言(Scala 也是如此)的非常普遍的做法,并将空对象的开销降至零,使任何针对该习惯用语的效率论点都毫无意义。请注意保留 HTML 标签。

4
对于JPA实体,我认为您应该使用可变列表进行初始化,将字段设置为私有,并仅提供公共getter以获取您的集合。只有这样才能确保lineItems永远不会为空。不必担心创建空列表,因为现代JVM在垃圾回收方面相当出色。总体来说:不要预先优化您的代码! - Stefan Haberl
我同意@StefanHaberl的观点,你不应该使用不可变集合来初始化实体字段。这会使得创建新实例和测试变得困难。如果你担心性能问题,可以尝试类似于new HashSet<>(0)的方法(https://dev59.com/pGMl5IYBdhLWcg3w1593)。 - csharpfolk
وˆ‘认ن¸؛Collections.emptyList()و¯”ImmutableList.of()و›´وک“çگ†è§£م€‚ - fbastien

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