16

尽管有FetchType.EAGERJOIN FETCH,但我还是有一段时间通过 JSF组件LazyInitalizationException将一些对象添加到集合中,例如在我的例子中是.@ManyToManyUISelectMany<p:selectManyMenu>

,@Entity IdentUserFetchType.EAGER:

@Column(name = "EMPLOYERS")
@ManyToMany(fetch = FetchType.EAGER, cascade= CascadeType.ALL)
@JoinTable(name = "USER_COMPANY", joinColumns = { @JoinColumn(name = "USER_ID") }, inverseJoinColumns = { @JoinColumn(name = "COMPANY_ID") })
private Set<Company> employers = new HashSet<Company>();

,@Entity CompanyFetchType.EAGER:

@ManyToMany(mappedBy="employers", fetch=FetchType.EAGER)
private List<IdentUser> employee;

JPQL,具有JOIN FETCH

public List<IdentUser> getAllUsers() {
    return this.em.createQuery("from IdentUser u LEFT JOIN FETCH u.employers WHERE u.enabled = 1 AND u.accountNonLocked=0 ").getResultList();
}

UISelectMany提交时导致异常 的 JSF组件:

<p:selectManyMenu value="#{bean.user.employers}" converter="#{entityConverter}">
    <f:selectItems value="#{bean.companies}" var="company" itemValue="#{company}" itemLabel="#{company.name}"/>
</p:selectManyMenu>

堆栈跟踪的相关部分:

org.hibernate.LazyInitializationException: failed to lazily initialize a collection, could not initialize proxy - no Session
    at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:566)
    at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:186)
    at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:545)
    at org.hibernate.collection.internal.PersistentSet.add(PersistentSet.java:206)
    at com.sun.faces.renderkit.html_basic.MenuRenderer.convertSelectManyValuesForModel(MenuRenderer.java:382)
    at com.sun.faces.renderkit.html_basic.MenuRenderer.convertSelectManyValue(MenuRenderer.java:129)
    at com.sun.faces.renderkit.html_basic.MenuRenderer.getConvertedValue(MenuRenderer.java:315)
    at org.primefaces.component.selectmanymenu.SelectManyMenuRenderer.getConvertedValue(SelectManyMenuRenderer.java:37)
    ...

这是如何引起的,我该如何解决?

4

2 回答 2

32

提交时,JSFUISelectMany组件需要创建一个全新的集合实例,并预先填充提交和转换后的值。它不会清除并重用模型中的现有集合,因为这可能会反映在对同一集合的其他引用中,或者可能会UnsupportedOperationException因为集合不可修改而失败,例如由Arrays#asList()or获得的集合Collections#unmodifiableList()

默认情况下,负责这一切的(和)组件MenuRenderer背后的渲染器将基于集合创建一个全新的集合实例。如果返回 Hibernate内部使用的 Hibernate 的实现来填充实体的集合属性,这将反过来失败。该方法即需要通过当前会话初始化底层代理,但没有,因为该作业不是在事务服务方法中执行的。UISelectManyUISelectOnegetClass().newInstance()LazyInitializationExceptiongetClass()PersistentCollectionadd()

要覆盖 的默认行为MenuRenderer,您需要通过组件的属性显式指定所需集合类型的 FQN 。对于属性,您要指定,对于属性,您要指定(或者如果排序不重要):collectionTypeUISelectManyListjava.util.ArrayListSetjava.util.LinkedHashSetjava.util.HashSet

<p:selectManyMenu ... collectionType="java.util.LinkedHashSet">

这同样适用于UISelectMany直接绑定到 Hibernate 管理的 JPA 实体的所有其他组件。例如:

<p:selectManyCheckbox ... collectionType="java.util.LinkedHashSet">
<h:selectManyCheckbox ... collectionType="java.util.LinkedHashSet">
<h:selectManyListbox ... collectionType="java.util.LinkedHashSet">
<h:selectManyMenu ... collectionType="java.util.LinkedHashSet">

另请参阅VDL 文档等<h:selectManyMenu>不幸的是, VDL 文档中<p:selectManyMenu>没有指定这一点,但由于它们使用相同的渲染器进行转换,所以它必须工作。如果 IDE 对某个未知collectionType属性进行抽搐并令人讨厌地强调它,即使它在您忽略'n'运行它时仍然有效,那么请改用它<f:attribute>

<p:selectManyMenu ... >
    <f:attribute name="collectionType" value="java.util.LinkedHashSet" />
    ...
</p:selectManyMenu>
于 2014-08-11T17:58:51.567 回答
6

editUserBehavior.currentUser.employers解决方法:用不被 Hibernate 管理的集合替换。

为什么?当实体成为托管实体时,Hibernate 会HashSet用它自己的Set(be it PersistentSet)实现替换你的。通过分析 JSF 的实现MenuRenderer,事实证明它在某一时刻Set反射性地创建了新的。请参阅中的评论MenuRenderer.convertSelectManyValuesForModel()

// 尝试反映一个无参数的构造函数并在可用时调用

在构造的过程中PersistentSet initialize()被调用 - 因为这个类只意味着从 Hibernate 调用 - LazyInitializationException 被抛出。

注意:这只是我的怀疑。我不知道您的 JSF 和 Hibernate 版本,但更可能是这种情况。

于 2014-08-11T14:43:57.553 回答