com.sun.faces.renderkit.html_basic.MenuRenderer.convertSelectManyValuesForModel上的org.hibernate.LazyInitializationException [英] org.hibernate.LazyInitializationException at com.sun.faces.renderkit.html_basic.MenuRenderer.convertSelectManyValuesForModel
问题描述
尽管有FetchType.EAGER
和JOIN FETCH
,但在通过JSF UISelectMany
组件向@ManyToMany
集合中添加一些对象(例如我的情况下为<p:selectManyMenu>
)时,却得到了LazyInitalizationException
.
Despite of FetchType.EAGER
and JOIN FETCH
, I get a LazyInitalizationException
while adding some objects to a @ManyToMany
collection via a JSF UISelectMany
component such as in my case the <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
组件在提交时导致异常:
The JSF UISelectMany
component causing the exception while submitting:
<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)
...
这是怎么引起的,我该如何解决?
How is this caused and how can I solve it?
推荐答案
提交时,JSF UISelectMany
组件需要创建集合的全新实例,并预先填充提交和转换后的值.它不会清除并重用模型中的现有集合,因为它可能会反映在对同一集合的其他引用中,或者可能会失败,并显示UnsupportedOperationException
,因为集合是不可修改的,例如通过Collections#unmodifiableList()
.
While submitting, the JSF UISelectMany
components need to create a brand new instance of the collection with the submitted and converted values prefilled. It won't clear out and reuse the existing collection in the model as that may either get reflected in other references to the same collection, or may fail with an UnsupportedOperationException
because the collection is unmodifiable, such as the ones obtained by Arrays#asList()
or Collections#unmodifiableList()
.
UISelectMany
(和 PersistentCollection
,Hibernate在内部使用它来填充实体的collection属性. add()
方法即需要通过当前会话初始化基础代理,但是没有什么用,因为该工作不是在事务服务方法中执行的.
The MenuRenderer
, the renderer behind UISelectMany
(and UISelectOne
) components who's responsible for this all, will by default create a brand new instance of the collection based on collection's getClass().newInstance()
. This would in turn fail with LazyInitializationException
if the getClass()
returns an implementation of Hibernate's PersistentCollection
which is internally used by Hibernate to fill the collection property of an entity. The add()
method namely needs to initialize the underlying proxy via the current session, but there's none because the job isn't performed within a transactional service method.
要覆盖MenuRenderer
的默认行为,您需要明确通过UISelectMany
组件的collectionType
属性指定所需集合类型的FQN.对于List
属性,您想要指定java.util.ArrayList
,对于Set
属性,您想要指定java.util.LinkedHashSet
(如果订购不重要,则可以指定java.util.HashSet
):
To override this default behavior of MenuRenderer
, you need to explicitly specify the FQN of the desired collection type via the collectionType
attribute of the UISelectMany
component. For a List
property, you'd like to specify java.util.ArrayList
and for a Set
property, you'd like to specify java.util.LinkedHashSet
(or java.util.HashSet
if ordering isn't important):
<p:selectManyMenu ... collectionType="java.util.LinkedHashSet">
这同样适用于直接绑定到Hibernate管理的JPA实体的所有其他UISelectMany
组件.例如:
The same applies to all other UISelectMany
components as well which are directly tied to a Hibernate-managed JPA entity. E.g:
<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>
等文档.不幸的是,这没有在 <p:selectManyMenu>
,但是由于它们使用相同的渲染器进行转换,因此它必须能够工作.如果IDE急于想起一个未知的collectionType
属性,即使它在您忽略'n'run时可以正常工作时,也会令人烦恼地强调该属性,请改用<f:attribute>
.
See also the VDL documentation of among others <h:selectManyMenu>
. This is unfortunately not specified in VDL documentation of <p:selectManyMenu>
, but as they use the same renderer for converting, it must work. If the IDE is jerking about an unknown collectionType
attribute and annoyingly underlines it even though it works when you ignore'n'run it, then use <f:attribute>
instead.
<p:selectManyMenu ... >
<f:attribute name="collectionType" value="java.util.LinkedHashSet" />
...
</p:selectManyMenu>
这篇关于com.sun.faces.renderkit.html_basic.MenuRenderer.convertSelectManyValuesForModel上的org.hibernate.LazyInitializationException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!