org.hibernate.LazyInitializationException at com.sun.faces.r



尽管有FetchType.EAGERJOIN FETCH,但我在通过 JSF UISelectMany组件将一些对象添加到 @ManyToMany 集合时得到了一个LazyInitalizationException,例如在我的例子中是 <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();
}

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

<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)
    ...

这是如何造成的,我该如何解决?

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

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

要覆盖 MenuRenderer 的默认行为,您需要通过UISelectMany组件的 collectionType 属性显式指定所需集合类型的 FQN。对于List媒体资源,您需要指定java.util.ArrayList,对于Set媒体资源,您需要指定java.util.LinkedHashSet(如果排序不重要,则指定java.util.HashSet(:

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

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

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

另请参阅<h:selectManyMenu>等的 VDL 文档。不幸的是,这在 <p:selectManyMenu> 的 VDL 文档中没有指定,但由于它们使用相同的渲染器进行转换,因此它必须工作。如果 IDE 对未知的 collectionType 属性大发雷霆,并且烦人地下划线,即使它在忽略它时有效,请改用<f:attribute>

<p:selectManyMenu ... >
    <f:attribute name="collectionType" value="java.util.LinkedHashSet" />
    ...
</p:selectManyMenu>

解决方案:将editUserBehavior.currentUser.employers替换为不受 Hibernate 管理的集合。

为什么?当实体成为托管实体时,休眠会将您的HashSet替换为其自己的Set实现(无论是PersistentSet(。通过分析JSF MenuRenderer的实现,事实证明,在某一时刻,它以反射的方式创建新的Set。请参阅MenuRenderer.convertSelectManyValuesForModel()中的评论

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

在构造期间,PersistentSet initialize()被调用,并且 - 由于此类仅用于从Hibernate调用 - 抛出LazyInitializationException。

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

相关内容

  • 没有找到相关文章

最新更新