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

Nonostante FetchType.EAGER e JOIN FETCH , ottengo una LazyInitalizationException mentre aggiungo alcuni oggetti a una raccolta @ManyToMany tramite un componente UISelectMany come nel mio caso

.

L’ @Entity IdentUser , con 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 employers = new HashSet(); 

La @Entity Company , con FetchType.EAGER :

 @ManyToMany(mappedBy="employers", fetch=FetchType.EAGER) private List employee; 

Il JPQL, con JOIN FETCH :

 public List getAllUsers() { return this.em.createQuery("from IdentUser u LEFT JOIN FETCH u.employers WHERE u.enabled = 1 AND u.accountNonLocked=0 ").getResultList(); } 

Il componente JSF UISelectMany che causa l’eccezione durante l’invio:

 

La parte rilevante della traccia dello stack:

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

Come è causato e come posso risolverlo?

Durante l’invio, i componenti di UISelectMany devono creare una nuova istanza della raccolta con i valori inviati e convertiti precompilati. Non cancellerà e riutilizzerà la collezione esistente nel modello in quanto potrebbe riflettersi in altri riferimenti alla stessa raccolta, oppure potrebbe non riuscire con una UnsupportedOperationException perché la raccolta non è modificabile, come quelli ottenuti da Arrays#asList() o Collections#unmodifiableList() .

Il MenuRenderer , il renderer dietro a componenti UISelectMany (e UISelectOne ) responsabili di tutto questo, creerà per impostazione predefinita una nuova istanza della raccolta basata sulla getClass().newInstance() della raccolta. getClass().newInstance() . Ciò a sua volta fallirebbe con LazyInitializationException se il getClass() restituisce un’implementazione di PersistentCollection di Hibernate che viene utilizzata internamente da Hibernate per riempire la proprietà della raccolta di un’ quadro. Il metodo add() bisogno di inizializzare il proxy sottostante tramite la sessione corrente, ma non ce n’è uno perché il lavoro non viene eseguito all’interno di un metodo di servizio transazionale.

Per sovrascrivere questo comportamento predefinito di MenuRenderer , è necessario specificare esplicitamente l’FQN del tipo di raccolta desiderato tramite l’attributo collectionType del componente UISelectMany . Per una proprietà List , si desidera specificare java.util.ArrayList e per una proprietà Set , si desidera specificare java.util.LinkedHashSet (o java.util.HashSet se l’ordine non è importante):

  

Lo stesso vale per tutti gli altri componenti di UISelectMany che sono direttamente collegati a un’ quadro JPA gestita da Hibernate. Per esempio:

     

Vedi anche la documentazione VDL tra gli altri . Questo sfortunatamente non è specificato nella documentazione VDL di , ma poiché usano lo stesso renderer per la conversione, deve funzionare. Se l’IDE si sta masturbando su un attributo collectionType sconosciuto e lo sottolinea in modo noioso, anche se funziona ignorandolo, quindi usa .

   ...  

Soluzione: sostituire editUserBehavior.currentUser.employers con raccolta non gestita da Hibernate.

Perché? Quando l’entity framework viene gestita, Hibernate sostituisce il tuo HashSet con la sua implementazione di Set (sia PersistentSet ). Analizzando l’implementazione di JSF MenuRenderer , si scopre che a un certo punto crea un nuovo Set riflessivo. Vedi il commento in MenuRenderer.convertSelectManyValuesForModel()

// prova a riflettere un costruttore senza argomenti e invoca se disponibile

Durante la costruzione di PersistentSet viene richiamato initialize() e, poiché questa class deve essere richiamata da Hibernate, viene lanciata LazyInitializationException.

Nota: questo è solo il mio sospetto. Non conosco le tue versioni di JSF e Hibernate, ma è più probabile che sia così.