springhibernatejpajboss6.xopen-session-in-view

Can't get OpenEntityManagerInViewFilter to work in JBoss 6.1


I am trying to add open-session-in-view behavior to an existing pure JPA application. Using Spring in the service-tier is not an option. I would like to wrap the view in Spring's OpenEntityManagerInViewFilter, and not have to modify the EJB layer.

I am not having any luck getting OpenEntityManagerInViewFilter (Spring 3.2.2) to work in JBoss 6.1. The filter is definitely being invoked, but I am still getting a LazyInitializationException in the view.

The filter and the session-bean are using a different instance (and class) of the EntityManager. The filter is getting a org.hibernate.ejb.EntityManagerImpl, while the session-bean is getting a org.jboss.jpa.tx.TransactionScopedEntityManager. I am not sure what Spring configuration is responsible for this.

Here is the relevant code/config:

war/WEB-INF/classes/test.web.servlet.TestServlet

public class TestServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

@EJB
private ServiceLocal service;

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {

    long parentId = Long.parseLong(req.getParameter("parentId"));
    Parent parent = service.retrieveParent(parentId);

    // this call throws a LazyInitializationException
    // because parent.children.session is NULL
    parent.getChildren().iterator().next().getName();

    req.setAttribute("parent", parent);
    RequestDispatcher requestDispatcher = this.getServletContext().getRequestDispatcher("/WEB-INF/jsp/view.jsp");
    requestDispatcher.forward(req, resp);
}
}

ejb/test.ejb.session.ServiceBean

@Stateless
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public class ServiceBean implements ServiceLocal, Service {

    @PersistenceContext(name="test")
    private EntityManager entityManager;

    @Override
    public Parent retrieveParent(Long parentId) {
        return entityManager.find(Parent.class, parentId);
    }
}

war/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    id="WebApp_ID" version="3.0">

<display-name>test-war</display-name>

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/spring.xml</param-value>
</context-param>

<filter>
    <filter-name>osiv-filter</filter-name>
    <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
    <init-param>
        <param-name>flushMode</param-name>
        <param-value>AUTO</param-value>
    </init-param>
</filter>

<servlet>
    <servlet-name>test-servlet</servlet-name>
    <servlet-class>test.web.servlet.TestServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>test-servlet</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>

<filter-mapping>
    <filter-name>osiv-filter</filter-name>
    <servlet-name>test-servlet</servlet-name>
</filter-mapping>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<listener>
    <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>

</web-app>

war/WEB-INF/spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN"
"http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
    </property>
    <property name="persistenceUnitName" value="test" />
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.transaction.manager_lookup_class">
                org.hibernate.transaction.JBossTransactionManagerLookup
            </prop>
        </props>
    </property>
</bean>
</beans>

ejb/META-INF/persistence.xml

<persistence
    xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">   
   <persistence-unit name="test" transaction-type="JTA">
      <provider>org.hibernate.ejb.HibernatePersistence</provider>
      <jta-data-source>java:/MSSQLDS</jta-data-source>
      <properties>
         <property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect"/>
         <property name="hibernate.show_sql" value="false" />
         <property name="hibernate.format_sql" value="true" />
         <property name="hibernate.use_sql_comments" value="true" />
         <property name="jboss.entity.manager.factory.jndi.name" value="java:/testEntityManagerFactory" />
         <property name="jboss.entity.manager.jndi.name" value="java:/testEntityManager" />
      </properties>
   </persistence-unit>
</persistence>

Solution

  • I don't think it's necessary to use a custom EntityManagerFactory to lookup the EntityManagerFactory via JDNI, the element should take care of that.

    I have given your setup a little bit more thought and i don't think that the spring OpenEntityManagerInViewFilter will work for you. It binds an entity manager to the current thread so that spring's transaction management code can reuse it. The problem is that spring doesn't handle the transaction management of your service bean as this is handled by the the application server. The application server doesn't detect the entity manager bound to the thread by spring and creates another one; resulting in 2 different instances.

    To make it work you should either define your service bean(s) in spring so that spring handles the transaction management or use jboss seam (JBoss Seam: How to Open jpa/hibernate session in view)