初始化或不初始化JPA关系映射?

时间:2013-12-21 03:39:29

标签: java hibernate jpa eclipselink openjpa

在一对多的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 个答案:

答案 0 :(得分:17)

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

为什么:因为订单可以有0,1或N行,并且最好使用空的,单一尺寸或N尺寸的集合建模。如果集合为null,则必须在代码中的任何位置检查它。例如,如果列表为null,则此简单循环将导致NullPointerException:

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

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

答案 1 :(得分:1)

我还建议使用Guava的不可变集合,例如,

import com.google.common.collect.ImmutableList;
// ...
@OneToMany(mappedBy="order")
List<LineItem> lineItems = ImmutableList.of();

这个习惯用法永远不会创建一个新的空列表,但会重用一个表示空列表的实例(类型无关紧要)。这是函数式编程语言的一种非常常见的做法(Scala也这样做)并且将减少为具有空对象而不是空值的开销,使得针对该成语的任何效率参数都没有实际意义。

答案 2 :(得分:0)

我更喜欢这样的实用程序:

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
});

答案 3 :(得分:0)

我的建议是不要初始化它们。

我们遇到了以下情况:初始化集合,然后基本上连续两次检索相同的实体。在第二次检索之后,应该已经有数据的延迟加载的集合在调用其getter之后为空。另一方面,如果我们在第一次检索后调用getter,则该集合确实加载了数据。从理论上讲,第二个检索从会话中获得了一个受管实体,该会话的集合被初始化为空,并且似乎已经被加载或被修改,因此没有延迟加载。解决的办法是不初始化集合。这样,我们可以在事务中多次检索实体,并正确加载其延迟加载的集合。

还有一点要注意:在不同的环境中,行为是不同的。当在同一事务中第二次检索到的实体上调用集合的getter时,集合的延迟加载就很好了。

不幸的是,我没有关于两种环境之间的区别的信息。似乎-尽管我们没有100%证明它并且没有确定实现-但不同的JPA实现在初始化集合方面的工作方式有所不同。

我们正在使用休眠模式-只是不知道我们在两个平台上都使用了哪个版本。