尽管有FetchType.EAGER
和JOIN FETCH
,但在通过JSF LazyInitalizationException
组件(例如本例中的@ManyToMany
)将某些对象添加到UISelectMany
集合时,却得到了<p:selectManyMenu>
。@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>();
@Entity Company
,带有FetchType.EAGER
:@ManyToMany(mappedBy="employers", fetch=FetchType.EAGER)
private List<IdentUser> employee;
带有
JOIN FETCH
的JPQL: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()
是负责所有这些工作的MenuRenderer
(和UISelectMany
)组件背后的渲染器,默认情况下,将基于集合的UISelectOne
创建该集合的全新实例。如果getClass().newInstance()
返回Hibernate的LazyInitializationException
的实现,而Hibernate在内部使用该实现来填充实体的collection属性,则getClass()
会失败。也就是说,PersistentCollection
方法需要通过当前会话来初始化基础代理,但是没有什么用,因为该工作不是在事务服务方法中执行的。
要覆盖add()
的默认行为,您需要通过MenuRenderer
组件的collectionType
属性显式指定所需集合类型的FQN。对于UISelectMany
属性,您想要指定List
,对于java.util.ArrayList
属性,您想要指定Set
(如果顺序不重要,则可以指定java.util.LinkedHashSet
):
<p:selectManyMenu ... collectionType="java.util.LinkedHashSet">
这同样适用于直接绑定到Hibernate管理的JPA实体的所有其他
java.util.HashSet
组件。例如:<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 documentation of among others
UISelectMany
。不幸的是,这没有在VDL documentation of <h:selectManyMenu>
中指定,但是由于它们使用相同的渲染器进行转换,因此它必须能够工作。如果IDE急于处理未知的<p:selectManyMenu>
属性,即使它在您忽略'n'run时起作用,它也会令人讨厌地强调它,请改用collectionType
。<p:selectManyMenu ... >
<f:attribute name="collectionType" value="java.util.LinkedHashSet" />
...
</p:selectManyMenu>
关于jsf - com.sun.faces.renderkit.html_basic.MenuRenderer.convertSelectManyValuesForModel上的org.hibernate.LazyInitializationException,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/25245426/