integralmind.org

»somewhere, something incredible is waiting to be known«

Java server faces and jpa lazy initialization exception

June 20th 2012

If you had the pleasure to be working on projects involving hibernate, spring AND Java Server Faces, i bet you have or will get in contact with the org.hibernate.LazyInitializationException.

The problem is, in my understanding in most of the cases that you want to access the database via the entity-manager without any session. In most of the cases this error occurs in combination with javax.persistence.FetchType.LAZY annotated entities.

Lets say you have two classes Patient and Doctor:

@Entity
public class Patient {
    @NotNull
    @Column(unique = true)
    private String name;

    @ManyToOne(fetch=FetchType.LAZY)
    private Doctor doctor;

    @PersistenceContext
    transient EntityManager entityManager;

    public static Patient findPatient(Long id) {
        if (id == null) return null;
        return entityManager().find(Patient.class, id);
    }

}

@Entity
public class Doctor {
    @NotNull
    @Column(unique = true)
    private String name;
}

As you see, patient references a lazy loaded doctor.

Assuming you have configured in the web.xml the OpenEntityManagerInViewFilter somewhat like this:

<filter>
    <filter-name>Spring OpenEntityManagerInViewFilter</filter-name>
    <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>Spring OpenEntityManagerInViewFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

It should be no problem to access the doctor field from patient and trigger the lazy loading, like this:

@RequestMapping("/patient")
@Controller
public class PatientController {

    @RequestMapping(value = "/{id}/doctor", method = RequestMethod.GET)
    @ResponseBody
    public ResponseEntity<byte[]> getDoctor(@PathVariable("id") Long id) {
        Patient p = Patient.findPatientById(id); // assume this is a simple finder / HQL select over patient
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "text/html");
        return new ResponseEntity<byte[]>(p.getDoctor().getId(), headers, HttpStatus.OK);
    }

}

Lets assume we have a patient with id 24 that has a doctor with id 48. if we now call http://localhost/patient/24/doctor/, we will get 48 as result. This works and is very logic to work.

Lets again assume we have configured java server faces with a faces-servelet and all the stuff required and have a simple view, lets call it patient.xhtml with something like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:c="http://java.sun.com/jstl/core"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:p="http://primefaces.org/ui">
<ui:composition>
        <ui:define name="content">  
            <h1>Patient</h1>
            <h:outputText value="#{patient.id}"/

            <h2>Patients Doctor</h2>
            <h:outputText value="#{patient.doctor.id}" />

        </ui:define>
    </ui:composition>
</html>

If you would put this together, open up the page, the call to patient.doctor.id will not result in 48 as we would expect it, but in a org.hibernate.LazyInitializationException. I am not quite sure, but my assumption is, that when the jsf is rendered, the entity-manager session is already closed by the OpenEntityManagerInViewFilter, because the jsf is rendered afterwards and is not in the same processing-chain.

I google’d around and found a ticket https://jira.springsource.org/browse/SPR-3179, according to what is written there, it seems that the automatically handling ot the entity-manager via a filter id not working with java server faces because the jsf lives in another place in the filter chain. Also in the ticket you see a proposed solution to integrate a modified version of this filter to make the entity-manager handling work.

To make long story short, i tried to take the proposed approach and it really works. My final solution was in the end to handle the connections manually because i ran into performance problems because i had to handle hundreds of open connections and the entity manager performed very bad in managing this. Anyway the solution with a separate jsf filter and lazy-loading works! I anyway had to modify the filters a little (where outdated in the ticket).

Here we go. We need 3 extra classes:

JsfCompatibleOpenEntityManagerInViewFilter:

import java.io.IOException;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.orm.jpa.EntityManagerFactoryUtils;
import org.springframework.orm.jpa.EntityManagerHolder;
import org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter;
import org.springframework.transaction.support.TransactionSynchronizationManager;

public class JsfCompatibleOpenEntityManagerInViewFilter extends OpenEntityManagerInViewFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        EntityManagerFactory emf = lookupEntityManagerFactory(request);
        boolean participate = false;

        if (TransactionSynchronizationManager.hasResource(emf)) {
            // Do not modify the EntityManager: just set the participate flag.
            participate = true;
        } else {
            logger.debug("Opening JPA EntityManager in OpenEntityManagerInViewFilter");
            try {
                EntityManager em = createEntityManager(emf);
                TransactionSynchronizationManager.bindResource(emf, new EntityManagerHolder(em));
            } catch (PersistenceException ex) {
                throw new DataAccessResourceFailureException("Could not create JPA EntityManager", ex);
            }
        }

        try {
            filterChain.doFilter(request, response);
        }

        finally {
            if (!participate) {
                if (TransactionSynchronizationManager.getResource(emf) != null) {
                    EntityManagerHolder emHolder = (EntityManagerHolder) TransactionSynchronizationManager.unbindResource(emf);
                    logger.debug("Closing JPA EntityManager in OpenEntityManagerInViewFilter");
                    EntityManagerFactoryUtils.closeEntityManager(emHolder.getEntityManager());
                }
            }
        }
    }
}

OpenEntityManagerInViewPhaseListener:

import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.persistence.EntityManagerFactory;

import org.apache.log4j.Logger;
import org.springframework.orm.jpa.EntityManagerHolder;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionSynchronizationManager;

/**
 * The OpenSessionInViewPhaseListener ensures that a transaction is active
 * during the JSF lifecycle.
 * 
 * A new transaction will be created if required before entering the
 * RESTORE_VIEW phase and will be committed or rolled back after the
 * INVOKE_APPLICATION phase.
 * 
 * Another read only transaction will be created before the RENDER_VIEW phase
 * and will be commited after the phase.
 * 
 * The structural code was strongly inspired by JBoss's
 * TransactionalSeamPhaseListener written by Gavin King. (except for the
 * transaction management part, which is Spring speficic)
 * 
 * @author Vincent Giguere - mailto: ---------------
 * @since February 20th 2007
 */
public class OpenEntityManagerInViewPhaseListener implements PhaseListener {

    private static final long serialVersionUID = 3742147752036645123L;

    private final static Logger logger = Logger.getLogger(OpenEntityManagerInViewPhaseListener.class);

    ThreadLocal<TransactionStatus> SYNCED_TRANSACTION_STATUS = new ThreadLocal<TransactionStatus>();

    // Spring support for transaction management
    PlatformTransactionManager transactionManager;

    EntityManagerFactory entityManagerFactory;

    public PlatformTransactionManager getTransactionManager() {
        return transactionManager;
    }

    public void setTransactionManager(PlatformTransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }

    /**
     * This method will create a new transaction if none is active. If the JSF
     * cycle is in a RENDER_RESPONSE phase, the transaction will be read-only.
     * 
     * @param phaseId
     *            The JSF lifecycle phase.
     */
    private void beginTransactionIfNoneActive(PhaseId phaseId) {
        try {
            if (!TransactionSynchronizationManager.isActualTransactionActive()) {

                boolean readOnlyTransaction = phaseId == PhaseId.RENDER_RESPONSE;

                if (logger.isDebugEnabled())
                    logger.debug("Beginin a new " + (readOnlyTransaction ? "read-only" : "write") + " Transaction.  Phase: " + phaseId);

                DefaultTransactionDefinition def = new DefaultTransactionDefinition();
                def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
                def.setReadOnly(readOnlyTransaction);

                if (!TransactionSynchronizationManager.hasResource(entityManagerFactory)) {
                    // bind new
                    TransactionSynchronizationManager.bindResource(entityManagerFactory, new EntityManagerHolder(entityManagerFactory.createEntityManager()));
                }
                SYNCED_TRANSACTION_STATUS.set(transactionManager.getTransaction(def));
            } else {
                if (logger.isDebugEnabled())
                    logger.debug("A Transaction is already active. Before phase" + phaseId);
            }

        } catch (TransactionException e) {
            throw new RuntimeException("failed to create or reuse entity manager with exception", e);
        }
    }

    /**
     * This method will commit or rollback the current transaction, depending on
     * its state. If an exception is thrown, it will be swallowed.
     * 
     * @param phaseId
     *            The phase ID in which this is taking place.
     */
    void commitOrRollbackActiveTransaction(PhaseId phaseId) {

        TransactionStatus status = SYNCED_TRANSACTION_STATUS.get();
        if (status != null) {
            try {
                if (!status.isCompleted() && !status.isRollbackOnly() && !TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
                    logger.debug("Committing transaction. phase: " + phaseId);
                    transactionManager.commit(status);
                } else if (status.isRollbackOnly() || TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
                    logger.debug("rolling back transaction after phase: " + phaseId);
                    transactionManager.rollback(status);
                }
            } catch (TransactionException e) {
                throw e;
            } finally {
                // due to multiple calls per page, its possible this gets called
                // twice right after another
                if (TransactionSynchronizationManager.getResource(entityManagerFactory)!=null) {
                    TransactionSynchronizationManager.unbindResource(entityManagerFactory);
                }
            }
        }
    }

    public void beforePhase(PhaseEvent event) {

        PhaseId phaseId = event.getPhaseId();
        logger.debug("Before phase: " + phaseId);

        try {
            if (phaseId == PhaseId.RESTORE_VIEW || phaseId == PhaseId.RENDER_RESPONSE) {
                beginTransactionIfNoneActive(phaseId);
            }
        } catch (Exception e) {
            logger.error("Exception thrown before phase:" + event.getPhaseId(), e);
        }
    }

    public PhaseId getPhaseId() {
        return PhaseId.ANY_PHASE;
    }

    /**
     * This method will invoke a transaction commit or rollback in the phase
     * INVOKE_APPLICATION or RENDER_RESPONSE is finished or if there was an
     * error rendering the view.
     */
    public void afterPhase(PhaseEvent event) {
        logger.debug("After phase: " + event.getPhaseId());

        if (event.getPhaseId() == PhaseId.INVOKE_APPLICATION || event.getFacesContext().getRenderResponse() || event.getFacesContext().getResponseComplete() || (event.getPhaseId() == PhaseId.RENDER_RESPONSE))

            commitOrRollbackActiveTransaction(event.getPhaseId());
        try {

        } catch (Exception e) {
            logger.error("Exception thrown after phase:" + event.getPhaseId(), e);
        }
    }

    public EntityManagerFactory getEntityManagerFactory() {
        return entityManagerFactory;
    }

    public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) {
        this.entityManagerFactory = entityManagerFactory;
    }

}

and OpenEntityManagerInViewPhaseListenerDelegator

import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

import org.apache.log4j.Logger;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.jsf.FacesContextUtils;


/**
 * This class is a PhaseListener that serves as a delegates to a
 * spring configured bean (also a PhaseListener) that will setup an
 * entity manager when JSF is going through its phases.
 * 
 * It is configured through the web.xml file, which must define a context-param name (key:openEntityManagerInViewPhaseListenerBeanName)
 * containing the name of the spring configured PhaseListener bean to which to delegate.
 * 
 * It is meant to reproduce the same pattern as the org.springframework.web.filter.DelegatingFilterProxy / org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter 
 * in non-JSF spring based JPA / WEB applications.
 * 
 * 
 * @author Vincent Giguere mailto: ---------------
 * 
 *   --- CONFIGURATION EXAMPLE ----
 * 
 * web.xml
 * <context-param>
 *  <param-name>openEntityManagerInViewPhaseListenerBeanName</param-name>
 *  <param-value>openEntityManagerInViewPhaseListener</param-value>
 * </context-param>
 * 
 * applicationContext.xml
 * <bean id="openEntityManagerInViewPhaseListener" class="com.covansys.infra.jpa.support.OpenEntityManagerInViewPhaseListener">
 *  <property name="entityManagerFactoryBeanName" value="entityManagerFactory" />
 * </bean>  
 * 
 * faces-config.xml
 * <lifecycle>
 *      <phase-listener>com.covansys.infra.jpa.support.OpenEntityManagerInViewPhaseListenerDelegator</phase-listener>
 * </lifecycle>
 * 
 */

public class OpenEntityManagerInViewPhaseListenerDelegator implements PhaseListener {


    private static final long serialVersionUID = 6174762137905374162L;
    private final static Logger logger = Logger.getLogger(OpenEntityManagerInViewPhaseListenerDelegator.class);

    //This is the key used in web.xml to identify the name of the spring bean to which to delegate.
    public static final String DELEGATE_BEAN_NAME_KEY = "openEntityManagerInViewPhaseListenerBeanName";
    private String delegatingBeanName = null;

    /**
    * Delegates the afterPhase to the delegate bean
    */
    public void afterPhase(PhaseEvent phaseEvent) {
        PhaseListener delegate = getDelegatingBean();
        if(delegate != null)
            delegate.afterPhase(phaseEvent);
        else
            logger.error("OpenEntityManagerInViewPhaseListenerDelegator is registered in faces-config, but its delegate is either not presend or misconfigured.");
    }

    public PhaseId getPhaseId() {
        return PhaseId.ANY_PHASE;
    }

    /**
    * Delegates the beforePhase to the delegate bean
    */
    public void beforePhase(PhaseEvent phaseEvent) {

        PhaseListener delegate = getDelegatingBean();
        if(delegate != null)
            delegate.beforePhase(phaseEvent);
        else
            logger.error("A OpenEntityManagerInViewPhaseListenerDelegator is registered in faces-config, but its delegate is either not presend or misconfigured.");
    }


    /**
     * This method looks up Spring containers trying to find the bean that matches the delegating bean name
     * @return A PhaseListener found under the specific name. Null if not found or not a PhaseListener
     */
    private PhaseListener getDelegatingBean() {
        String beanName = getDelegatingBeanName();
        if (beanName == null) {
            logger.error("OpenEntityManagerInViewPhaseListenerDelegator could not find OpenEntityManagerInViewPhaseListener bean. Read the javadoc on OpenEntityManagerInViewPhaseListenerDelegator and Review your web.xml, faces-config.xml and applicationContext.xml files.");
            return null;
        }

        Object delegateBean = getWebApplicationContext().getBean(beanName);
        if (!(delegateBean instanceof PhaseListener)) {
            logger.error("OpenEntityManagerInViewPhaseListenerDelegator found a bean named ["+beanName+ "] but it is not a javax.faces.event.PhaseListener. Review your configuration");
            return null;
        }
        return (PhaseListener) delegateBean;
    }

    /**
     * This method looks up the servlet context parameters to retrieve the name of the delegate bean in Spring.
     * @return A String refering the the delegate bean name in Spring
     */
    private String getDelegatingBeanName() {
        if (this.delegatingBeanName == null) {
            WebApplicationContext ctx = getWebApplicationContext();
            this.delegatingBeanName = ctx.getServletContext().getInitParameter(OpenEntityManagerInViewPhaseListenerDelegator.DELEGATE_BEAN_NAME_KEY);
        }
        return this.delegatingBeanName;

    }

    private WebApplicationContext getWebApplicationContext() {
        return FacesContextUtils.getRequiredWebApplicationContext(FacesContext.getCurrentInstance());
    }
}

Credits go to Vincent Giguere who originally developed the idea behind this solution (vince, i removed you email from the source, to beware of spam ^_). Thanks man, good work! I also modified the original source a little to work with latest dependencies.

To integrate the filter you have to modify some xmls, here es what goes where.

OpenEntityManagerInViewPhaseListenerDelegator => faces-config.xml
OpenEntityManagerInViewPhaseListener => applicationContext.xml
JsfCompatibleOpenEntityManagerInViewFilter => web.xml

At first the faces-config.xml

<?xml version="1.0"?>
<faces-config xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd" 
    version="2.0">
    <application>
        ...
    </application>
    <converter>
        ...
    </converter>
    <!-- ADD THIS -->
    <lifecycle>
        <phase-listener>your.package.filter.OpenEntityManagerInViewPhaseListenerDelegator</phase-listener>
    </lifecycle>
</faces-config>

and applicationContext.xml (of course you should have defined entityManagerFactory and transactionManager somewhere):

<bean id="openEntityManagerInViewPhaseListener" class="your.package.filter.OpenEntityManagerInViewPhaseListener">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
    <property name="transactionManager" ref="transactionManager"/>
</bean> 

at last, web.xml, here is also where the modification of the original aproach takes place:

<!-- OUTCOMMENT ORIGINAL SPRING FILTER 
 <filter>
    <filter-name>Spring OpenEntityManagerInViewFilter</filter-name>
    <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>Spring OpenEntityManagerInViewFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
-->

<!-- IS REPLACED BY A MODIFIED VERSION -->

 <filter>
    <filter-name>MyJsfCompatibleOpenEntityManagerInViewFilter</filter-name>
    <filter-class>your.package.filter.JsfCompatibleOpenEntityManagerInViewFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>MyJsfCompatibleOpenEntityManagerInViewFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

also in web.xml you have to tell the OpenEntityManagerInViewPhaseListenerDelegator where the intercepted filter for handling the sessions, comes from, it goes like this:

<context-param>
    <param-name>openEntityManagerInViewPhaseListenerBeanName</param-name>
    <param-value>openEntityManagerInViewPhaseListener</param-value>
</context-param>

This should do the trick, goodbye Lazy-Loading-Exceptions! But as you see, this is not a very simple nor elegant solution and maybe requires you to think about the whole life-cycle of how jsf is processed and modify some snippets. In the above versions, you see that i messed with logger.debug(.... If you face any problems, try to follow the debugging output, put your breakpoints and try to follow how the processing is working, it helps to understand what is happening.

The JsfCompatibleOpenEntityManagerInViewFilter is my invention. It is a modified version of springs original OpenEntityManagerInViewFilter. It not just stupidly closes any open sessions, but will check if open sessions are existing. Otherwise you would get exceptions on closing already closed sessions (because we maybe already closed sessions with the new OpenEntityManagerInViewPhaseListener).

Hope this will help you at least to understand where the problem comes from, i took me hours to find out why the heck JSF didn’t handled lazy-loaded-references as expected. Would be interesting to see if this will solve somebodies problems with lazy-loading and if you maybe found a way to improve it or modified it in a useful way.

Beware of the Lazy!