Blog

Friday, 1 February 2013

JSF, ManyToMany and LazyInitializationException


Recently I needed to create simple CRUD.
When I tried to modify collection of available entities with annotation @ManyToMany my application thr
ew failed to lazily initialize a collection, no session or session was closed: org.hibernate.LazyInitializationException: (…)”

Entities:
...

@Entity
@Table(name = "DISEASE")
public class Disease implements Serializable {
    @Id
    @GeneratedValue
    private Long id;

    @NotNull
    @Length(max = 100)
    @Column(name = "NAME", length = 100, nullable = false)
    private String name;

    @NotNull
    @ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH})
    private List<Symptom> symptoms = new ArrayList<Symptom>();

...
...

@Entity
@Table(name = "SYMPTOM")
public class Symptom implements Serializable {

    @Id
    @GeneratedValue
    private Long id;

    @NotNull
    @Length(max = 100)
    @Column(name = "NAME", length = 100, nullable = false)
    private String name;

...


My facelet code:

...   

                
      
            
            
               
     
...

And stack trace error:

org.hibernate.LazyInitializationException: failed to lazily initialize a collection, no session or session was closed
 at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:393)
 at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:385)
 at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:378)
 at org.hibernate.collection.internal.AbstractPersistentCollection.write(AbstractPersistentCollection.java:208)
 at org.hibernate.collection.internal.PersistentBag.add(PersistentBag.java:291)
 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 javax.faces.component.UIInput.getConvertedValue(UIInput.java:1030)
 at javax.faces.component.UIInput.validate(UIInput.java:960)
 at javax.faces.component.UIInput.executeValidate(UIInput.java:1233)
 at javax.faces.component.UIInput.processValidators(UIInput.java:698)
 at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1214)
 at javax.faces.component.UIForm.processValidators(UIForm.java:253)
 at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1214)
 at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1214)
 at javax.faces.component.UIViewRoot.processValidators(UIViewRoot.java:1172)
 at com.sun.faces.lifecycle.ProcessValidationsPhase.execute(ProcessValidationsPhase.java:76)
 at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
 at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
 at javax.faces.webapp.FacesServlet.service(FacesServlet.java:593)
 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:329)
 at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248)
 at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:275)
 at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:161)
 at org.jboss.as.jpa.interceptor.WebNonTxEmCloserValve.invoke(WebNonTxEmCloserValve.java:50)
 at org.jboss.as.web.security.SecurityContextAssociationValve.invoke(SecurityContextAssociationValve.java:153)
 at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:155)
 at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
 at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
 at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:368)
 at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:877)
 at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:671)
 at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:930)
 at java.lang.Thread.run(Thread.java:662)

At first I thought it was caused by FetchType attribute in my entity so I set “fetch = FetchType.EAGER”, but this didn’t help. In stack trace we can see that method when error occurred is called getConvertedValue() from MenuRendered class (from jsf-impl-2.1.7-jbossorg-2.jar). I needed to add Jboss Nexus repository to Maven settings file (settings.xml) and next downloaded sources and documentation to maven.
Then I could see exactly what caused the error:


...

    protected Object convertSelectManyValuesForModel(FacesContext context,
                                                     UISelectMany uiSelectMany,
                                                     Class modelType,
                                                     String[] newValues) {

        if (modelType.isArray()) {
            return convertSelectManyValues(context,
                                           uiSelectMany,
                                           modelType,
                                           newValues);
        } else if (Collection.class.isAssignableFrom(modelType)) {
            Object[] values = (Object[]) convertSelectManyValues(context,
                                                                 uiSelectMany,
                                                                 Object[].class,
                                                                 newValues);

            Collection targetCollection = null;

            // see if the collectionType hint is available, if so, use that
            Object collectionTypeHint = uiSelectMany.getAttributes().get("collectionType");
            if (collectionTypeHint != null) {
                targetCollection = createCollectionFromHint(collectionTypeHint);
            } else {
                // try to get a new Collection to store the values based
                // by trying to create a clone
                Collection currentValue = (Collection) uiSelectMany.getValue();
                if (currentValue != null) {
                    targetCollection = cloneValue(currentValue);
                }

    ...

Problem was that jsf tried to clone collection (line 30).

Solution was simple, I had to set attribute collectionType in selectManyCheckbox component to “java.util.ArrayList”:



  
                    
                

Alternatively it could be done like this: