org.hibernate.LazyInitializationException at com.sun.faces.renderkit.html_basic.MenuRenderer.convertSelectManyValuesForModel

一世执手 提交于 2019-11-27 08:24:28

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

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.

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">

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">

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>

Solution: Replace the editUserBehavior.currentUser.employers with collection that is not managed by Hibernate.

Why? When the Entity becomes managed, the Hibernate replaces your HashSet with its own implementation of Set (be it PersistentSet). By analysing the implementation of JSF MenuRenderer, it turns out that at one point it creates new Set reflectively. See the comment in MenuRenderer.convertSelectManyValuesForModel()

// try to reflect a no-argument constructor and invoke if available

During construction of PersistentSet initialize() is invoked and - as this class is only meant to be invoked from Hibernate - LazyInitializationException is thrown.

Note: This is my suspicion only. I don't know your versions of JSF and Hibernate but this is more likely the case.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!