Jsf org.hibernate.LazyInitializationException位于com.sun.faces.renderkit.html_basic.menurender.convertSelectManyValuesForModel

Jsf org.hibernate.LazyInitializationException位于com.sun.faces.renderkit.html_basic.menurender.convertSelectManyValuesForModel,jsf,jpa,lazy-loading,eager-loading,selectmanymenu,Jsf,Jpa,Lazy Loading,Eager Loading,Selectmanymenu,尽管存在FetchType.EAGER和JOIN FETCH,但在通过JSFUISelectMany组件(如我的例子中的将一些对象添加到@manytomy集合时,我还是得到了LazyInitalizationException @Entity IdentUser,带有FetchType.EAGER: @Column(name = "EMPLOYERS") @ManyToMany(fetch = FetchType.EAGER, cascade= CascadeType.ALL) @JoinTab

尽管存在
FetchType.EAGER
JOIN FETCH
,但在通过JSF
UISelectMany
组件(如我的例子中的
将一些对象添加到
@manytomy
集合时,我还是得到了
LazyInitalizationException

@Entity IdentUser
,带有
FetchType.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>();
@ManyToMany(mappedBy="employers", fetch=FetchType.EAGER)
private List<IdentUser> employee;
JPQL,带有
连接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();
}
堆栈跟踪的相关部分:

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

这是如何造成的以及我如何解决它的?

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

为什么??当实体被管理时,Hibernate将用自己的
实现替换您的
哈希集
(可以是
持久集
)。通过分析JSF
menurender
的实现,可以发现它在某一点上反射性地创建了新的
Set
。请参见
menurender.convertSelectManyValuesForModel()中的注释

//尝试反映无参数构造函数并调用(如果可用)

在构建
PersistentSet
initialize()
的过程中被调用,并且-由于该类仅用于从Hibernate调用-引发LazyInitializationException


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

提交时,JSF
UISelectMany
组件需要创建一个全新的集合实例,并预先填充提交和转换的值。它不会清除并重用模型中的现有集合,因为这些集合可能会反映在对同一集合的其他引用中,或者可能会因
不支持操作异常而失败,因为该集合是不可修改的,例如通过
数组#asList()
集合#不可修改列表()获得的集合

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

要覆盖
menurender
的此默认行为,需要通过
UISelectMany
组件的
collectionType
属性显式指定所需集合类型的FQN。对于
列表
属性,您需要指定
java.util.ArrayList
,对于
集合
属性,您需要指定
java.util.LinkedHashSet
(或者
java.util.HashSet,如果顺序不重要):

另见。遗憾的是,中没有指定这一点,但由于它们使用相同的渲染器进行转换,因此必须能够正常工作。如果IDE对未知的
collectionType
属性感到不安,并且在忽略“不”运行它的情况下仍然在它下面加下划线,那么请改用

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

...

@Tomczak感谢您的解释,以便更好地了解问题。我认为这取决于JPA+Hibernate,没有关注JSF。我相信,您提供的解决方案会起作用,但这不是我喜欢的方式(使用BOs而不是实体)。在这里,我更喜欢“BalusC”解决方案。。。非常感谢,对我来说效果很好。a.o.的VDL是什么?@Koray:我澄清了答案。
<p:selectManyMenu ... collectionType="java.util.LinkedHashSet">
<p:selectManyCheckbox ... collectionType="java.util.LinkedHashSet">
<h:selectManyCheckbox ... collectionType="java.util.LinkedHashSet">
<h:selectManyListbox ... collectionType="java.util.LinkedHashSet">
<h:selectManyMenu ... collectionType="java.util.LinkedHashSet">
<p:selectManyMenu ... >
    <f:attribute name="collectionType" value="java.util.LinkedHashSet" />
    ...
</p:selectManyMenu>