Question

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>
Was it helpful?

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)

OTHER TIPS

If you are sure that the open session in view filter is working then you might want to take a look at the transaction demarcation for service.retrieveParent(parentId);. The LazyInitializationException would make sense if the service/dao uses a different persistence context to load the parent entity.

I am not familiar with the spring/jboss setup and therefore missed some things.

I assume that your service bean is managed by the JBoss server and not by spring. By defining the LocalContainerEntityManagerFactoryBean you are actually configuring an EntityManagerFactory in Spring (very similar to the one managed by the application server). The open session in view filter is injected with the an EntityManager that's managed by Spring while it should have been injected by an entity manager managed by the application server.

I think that the following spring configuration will fix your problem (don't forget to adjust the jndi-name attribute):

<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:context="http://www.springframework.org/schema/context"
   xmlns:tx="http://www.springframework.org/schema/tx"
   xmlns:jee="http://www.springframework.org/schema/jee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
    http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd">
<context:annotation-config/>
<jee:jndi-lookup id="entityManagerFactory" jndi-name="persistence-units/test"/>
<tx:jta-transaction-manager/>

LazyInitializationException means that hibernate tried to get the data of a lazy collection/object but the session was already closed.

If this line parent.getChildren().iterator().next().getName(); is throwing the error, it means that childrens is a lazy collection with actually no useful data, and by calling iterator().next() hibernate tried to load the entire collection.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top