我在我的应用程序中使用Spring和JPA,但最近我对使用DomainObjects的集合感到有些困惑。
例如:
想象一下,我们有两个表:Parent和Child。父母可以有几个孩子(@OneToMany),孩子只能有一个孩子(@ManyToOne)。我使用Spring来处理我的ServiceLayer中的事务上下文(不是Dao - 工作单元模式),它在context-application.xml中由aop:config配置。子集合标记为LazyLoading Collection。到目前为止,一切都很好。
现在的问题如下:
我的服务中需要一个方法,它创建一个新的Parent并在同一个事务中调用另一个方法,用新创建的Parent进行一些计算。在这些计算中,我必须调用parent.getChildren()。显然这应该返回一个空Set,因为新的Parent在创建时没有任何Children,但由于某种原因我得到'null'而不是空集,这导致NullpointerException
要创建父级,我尝试了以下EntityManager方法:
两者都导致了空集。但我很确定,这些调用是在同一个事务中,因为在事务之外,我会收到一个LazyLoadingException(而不是NullPointer),此时(afaik)正确。另一方面,使用已经存在的parentManager.findById()的父项导致了一个现有的集合(这意味着它的工作方式与预期的一样)。为了解决这个问题,我尝试了几种方法,直到找到一个有效的方法:
合并/持久化后entityManager.refresh(parent),因为在调用refresh时集合会被初始化。
但在教程中我从未见过这种方法正在被使用。在EntityManager的JavaDoc中,已经说过,合并返回新的托管bean,而persist使引用本身得到管理。我猜想托管意味着,我已经可以在事务中使用这些集合了。为什么集合在创建时没有被初始化?我是否必须在Parent的构造函数中自己初始化它们?
我想知道,如果我做错了什么,或者在创建新的Parent实例后调用refresh()是正确的方法吗?如果这样猜测我只需要在持久化和更新之间分开,就像在下面的文章中建议的那样:JPA Implemenentations并在持久化后刷新新创建的对象?或者,当我真的需要使用Collection时,我应该刷新Object吗?
希望你能跟随我,我希望有任何建议或解决方案让我明白(呃)。
非常感谢,
ymene
答案 0 :(得分:4)
您是否初始化了收藏字段?
@OneToMany
private Set<Foo> foos = new HashSet<Foo>();
而不是
@OneToMany
private Set<Foo> foos;
因为后者当然会抛出NPE。
答案 1 :(得分:0)
尝试在父类中使用CascadeType.ALL。
@OneToMany(cascade = CascadeType.ALL)
public List<Child> getChildren() {
return children;
}
其次使用OpenSessionInViewFilter。这将确保您甚至可以从视图中调用parent.getChildren(),并且hibernate会话保持打开状态,直到响应被发回。以下是如何在web.xml中配置它
<filter>
<filter-name>OpenSessionInViewFilter</filter-name>
<filter-class>com.isavera.hibernate.OpenSessionFilter</filter-class>
<init-param>
<param-name>singleSession</param-name>
<param-value>false</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>OpenSessionInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
希望这有帮助