0

First some info:

  • I'm using:
    • Spring 3.1.1.RELEASE
    • Hibernate 4.1.5.SP1
    • JSF 2.0?
    • OpenSessionInViewFilter (org.springframework.orm.hibernate4.support.OpenSessionInViewFilter)
    • PrimeFaces 3.3.1
    • Lombok 0.11.2
    • JBoss 7.1.1.Final

The short version:

When my entity Emloyee becomes detached (and thus it's eagerly fetched collection of Locations too), I cannot call Employee.getLocations().add() without getting a LazyInitializationException. Using a non-JPA based collection to act as a transfer object works.

The Long version:

I have an entity Employee that has a collection of Locations:

@Data
@EqualsAndHashCode(callSuper = true, of = {})
@ToString(callSuper = true, of = {})
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Employee extends Person
{
    @ManyToMany
    @JoinTable(name = "location_employee",
            joinColumns = @JoinColumn(name = "employee_id",
                    referencedColumnName = "id"),
            inverseJoinColumns = @JoinColumn(name = "location_id",
                    referencedColumnName = "id"))
    private Set<Location>   locations   = new HashSet<Location>();
}

Employee extends from Person which has some strings like name, etc. but these are not relevant.

@Data
@EqualsAndHashCode(of = "id")
@ToString(of = { "id", "name" })
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Person
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Setter(AccessLevel.NONE)
    private Long    id;

    private String  name;
}

The Location class too is very simple:

@Data
@EqualsAndHashCode(of = "id")
@ToString(of = { "id", "name" })
@Entity
public class Location
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Setter(AccessLevel.NONE)
    private Long            id;

    private String          name;
    @ManyToMany
    @JoinTable(name = "location_employee",
            joinColumns = @JoinColumn(name = "location_id",
                    referencedColumnName = "id"),
            inverseJoinColumns = @JoinColumn(name = "employee_id",
                    referencedColumnName = "id"))
    private Set<Employee>   employees = new HashSet<Employee>();
}

All collections are lazy by default. However in my JSF page I want to add Locations to an existing Employee. To do this I eager load my Employee using the following backing bean:

@Named
@Scope("view")
public class BackingBean
{
    @Inject
    private EmployeeDAO employeeDAO;
    private Employee        employeeFull;
    @Inject
    private LocationDAO locationDAO;
    private List<Location> locations;

    public Employee getSelectedEmployeeFull()
    {
        if (selectedEmployeeFull == null)
        {
            selectedEmployeeFull = employeeDAO.getEagerById(1L);
        }
        return selectedEmployeeFull;
    }

    public void setEmployeeFull(Employee e)
    {
        employeeFull = e;
    }

    public List<Location> getLocations()
    {
        if (locations == null)
        {
            locations = locationDAO.getAll()
        }
        return locations;
    }

    //...
} 

The EmployeeDAO class including the eager load query:

@Named
public class EmployeeDAO
{
    @Inject
    private SessionFactory sessionFactory;

    @Transactional(readOnly = true)
    public Employee getEager(Long id)
    {
        Query q = sessionFactory.getCurrentSession().createQuery("select e from Employee e join fetch e.locations where e.id = :id");
        q.setParameter("id", id);
        try
        {
            return (Employee) q.uniqueResult();
        }
        catch (NonUniqueObjectException ex)
        {
            //Exception logging/handling
            return null;
        }
    }
}

In my JSF I have the following snippet:

<p:dialog dynamic="true">
    <h:form>
        <p:selectCheckboxMenu label="Locations" value="#{employeeBean.employeeFull.locations}">
            <f:selectItems var="location" itemLabel="#{location.name}" value="#{employeeBean.locations}" />
        </p:selectCheckboxMenu>

        <p:commandButton value="save" action="#{employeeBean.update}"/>
    </h:form>
</p:dialog>

When I open the dialog it loads the employeeFull property fine, and fills the selectCheckboxMenu with Locations and marks the Locations the Employee already has.

However when I click the save button (without changing anything) and the form gets submitted the Employee.locations collection trows a LazyInitializationException:

09:36:42,633 WARNING [javax.enterprise.resource.webcontainer.jsf.lifecycle] (http-localhost-127.0.0.1-8080-1) failed to lazily initialize a collection, no session or session was closed: org.hibernate.LazyInitializationException: failed to lazily initialize a collection, no session or session was closed
    at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:394) [hibernate-core-4.1.5.SP1.jar:4.1.5.SP1]
    at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:386) [hibernate-core-4.1.5.SP1.jar:4.1.5.SP1]
    at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:379) [hibernate-core-4.1.5.SP1.jar:4.1.5.SP1]
    at org.hibernate.collection.internal.PersistentSet.add(PersistentSet.java:206) [hibernate-core-4.1.5.SP1.jar:4.1.5.SP1]
    at com.sun.faces.renderkit.html_basic.MenuRenderer.convertSelectManyValuesForModel(MenuRenderer.java:382) [jsf-impl-2.1.7-jbossorg-2.jar:]
    at com.sun.faces.renderkit.html_basic.MenuRenderer.convertSelectManyValue(MenuRenderer.java:129) [jsf-impl-2.1.7-jbossorg-2.jar:]
    at com.sun.faces.renderkit.html_basic.MenuRenderer.getConvertedValue(MenuRenderer.java:315) [jsf-impl-2.1.7-jbossorg-2.jar:]
    at org.primefaces.component.selectcheckboxmenu.SelectCheckboxMenuRenderer.getConvertedValue(SelectCheckboxMenuRenderer.java:34) [primefaces-3.3.1.jar:]
    at javax.faces.component.UIInput.getConvertedValue(UIInput.java:1030) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at javax.faces.component.UIInput.validate(UIInput.java:960) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at javax.faces.component.UIInput.executeValidate(UIInput.java:1233) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at javax.faces.component.UIInput.processValidators(UIInput.java:698) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1214) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at javax.faces.component.UIForm.processValidators(UIForm.java:253) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1214) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at org.primefaces.component.dialog.Dialog.processValidators(Dialog.java:359) [primefaces-3.3.1.jar:]
    at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1214) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at javax.faces.component.UIViewRoot.processValidators(UIViewRoot.java:1172) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at com.sun.faces.lifecycle.ProcessValidationsPhase.execute(ProcessValidationsPhase.java:76) [jsf-impl-2.1.7-jbossorg-2.jar:]
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101) [jsf-impl-2.1.7-jbossorg-2.jar:]
    at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118) [jsf-impl-2.1.7-jbossorg-2.jar:]
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:593) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:329) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248) [jbossweb-7.0.13.Final.jar:]
    at org.primefaces.webapp.filter.FileUploadFilter.doFilter(FileUploadFilter.java:79) [primefaces-3.3.1.jar:]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:280) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248) [jbossweb-7.0.13.Final.jar:]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:322) [spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:116) [spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83) [spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113) [spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103) [spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113) [spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:54) [spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45) [spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter(BasicAuthenticationFilter.java:150) [spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter.doFilter(DefaultLoginPageGeneratingFilter.java:91) [spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:182) [spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105) [spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87) [spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:184) [spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:155) [spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346) [spring-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:259) [spring-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:280) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248) [jbossweb-7.0.13.Final.jar:]
    at org.springframework.orm.hibernate4.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:119) [spring-orm-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76) [spring-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:280) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:275) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:161) [jbossweb-7.0.13.Final.jar:]
    at org.jboss.as.web.security.SecurityContextAssociationValve.invoke(SecurityContextAssociationValve.java:153) [jboss-as-web-7.1.1.Final.jar:7.1.1.Final]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:155) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:368) [jbossweb-7.0.13.Final.jar:]
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:877) [jbossweb-7.0.13.Final.jar:]
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:671) [jbossweb-7.0.13.Final.jar:]
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:930) [jbossweb-7.0.13.Final.jar:]
    at java.lang.Thread.run(Thread.java:662) [rt.jar:1.6.0_31]

The EmployeeBean.update method is never called as the breakpoint set on it is never hit.

Now for the grand finale: How can I prevent this LazyInitializationException.

EDIT

I have created a workaround, but this is probably not the best solution:

In the JSF page:

<p:dialog dynamic="true" onShow="loadSelectedEmployee()">
    <h:form>
        <p:remoteCommand action="#{employeeBean.loadSelectedEmployee}" name="loadSelectedEmployee" update=":editEmployeeDialogContent" />
    </h:form>
    <p:outputPanel layout="block" id="editEmployeeDialogContent">
        <h:form rendered="#{employeeBean.selectedEmployeeLoaded}">
            <p:selectCheckboxMenu label="Locations" value="#{employeeBean.selectedEmployee.locations}">
                <f:selectItems var="location" itemLabel="#{location.name}" value="#{employeeBean.locations}" />
            </p:selectCheckboxMenu>

            <p:commandButton value="save" action="#{employeeBean.update}"/>
        </h:form>
    </p:outputPanel>
</p:dialog>

In the backing bean:

public void loadSelectedEmployee()
{
    if (!selectedEmployeeLoaded)
    {
        selectedEmployee.setLocations(locationManager
                .getByEmployee(selectedEmployee));
        selectedEmployee.setRoles(roleManager
                .getByEmployee(selectedEmployee));
        selectedEmployeeLoaded = true;
    }
}
6
  • Try tracking down the problem by removing unnecessary things like converter etc.. Commented Aug 28, 2012 at 7:13
  • @JMelnik I removed the converter, the stacktrace differs only from line 4 to line 12. The problem rises when JSF is calling the PersistentBag.add() method while the bag is disconnected. Commented Aug 28, 2012 at 7:33
  • Try checking with hibernate if collection is initialized, put it somewhere in your code (look stacktrace). how to check: stackoverflow.com/a/4306498/685962 Commented Aug 28, 2012 at 7:59
  • @JMelnik When the eager query is executed I see the locations collection being fetched with a join. Then when I call Hibernate.isInitialized() on the collection, it returns true. No additional query is fired. Using a non-JPA collection works by the way (I'm updating my question within seconds). Commented Aug 28, 2012 at 8:12
  • It may be unnecessary, but try also changing Set to Collection = ArrayList Commented Aug 28, 2012 at 10:21

2 Answers 2

1

This is definitly a bug and I guess either in Primefaces or in Jboss-jsf-api-2.1. I can modify the PersistenSet directly in the controller, so its definitly eager loaded, but when bound to the JSF component (I am using Primefaces selectCheckboxMenu) it's also throwing that LazyInitalizationException.

Sign up to request clarification or add additional context in comments.

Comments

0

If you do a HQL over an entity that has no relationship to marked "eager" will continue the brand of "lazy" and attempt to query the data at the time of "get".

For this case an example of forcing initialize the relationship would be:

Hibernate.initialize(e.getLocations());

Integrated:

@Transactional(readOnly = true)
public Employee getEager(Long id)
{
    Query q = sessionFactory.getCurrentSession().createQuery("select e from Employee e join fetch e.locations where e.id = :id");
    q.setParameter("id", id);
    try
    {
        final Employee e = (Employee) q.uniqueResult();
        if(e != null){
            Hibernate.initialize(e.getLocations());
        }
        return e;

    }
    catch (NonUniqueObjectException ex)
    {
        //Exception logging/handling
        return null;
    }
}

Regads,

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.