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
,但在通过JSFUISelectMany
组件(如我的例子中的
将一些对象添加到@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将用自己的集
实现替换您的哈希集
(可以是持久集
)。通过分析JSFmenurender
的实现,可以发现它在某一点上反射性地创建了新的Set
。请参见menurender.convertSelectManyValuesForModel()中的注释
//尝试反映无参数构造函数并调用(如果可用)
在构建PersistentSet
initialize()
的过程中被调用,并且-由于该类仅用于从Hibernate调用-引发LazyInitializationException
注:这只是我的怀疑。我不知道您的JSF和Hibernate版本,但更可能是这样。提交时,JSFUISelectMany
组件需要创建一个全新的集合实例,并预先填充提交和转换的值。它不会清除并重用模型中的现有集合,因为这些集合可能会反映在对同一集合的其他引用中,或者可能会因不支持操作异常而失败,因为该集合是不可修改的,例如通过数组#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>