4

我正在尝试将 open-session-in-view 行为添加到现有的纯 JPA 应用程序。在服务层中使用 Spring 不是一种选择。我想将视图包装在 Spring 的 OpenEntityManagerInViewFilter 中,而不必修改 EJB 层。

我没有任何运气让 OpenEntityManagerInViewFilter (Spring 3.2.2) 在 JBoss 6.1 中工作。过滤器肯定被调用了,但我仍然在视图中得到一个 LazyInitializationException 。

过滤器和会话 bean 正在使用 EntityManager 的不同实例(和类)。过滤器得到一个org.hibernate.ejb.EntityManagerImpl,而会话 bean 得到一个org.jboss.jpa.tx.TransactionScopedEntityManager. 我不确定 Spring 配置对此负责。

这是相关的代码/配置:

战争/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);
    }
}

战争/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>

战争/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>
4

4 回答 4

2

我认为没有必要使用自定义 EntityManagerFactory 来通过 JDNI 查找 EntityManagerFactory,该元素应该负责这一点。

我对您的设置进行了更多考虑,我认为 spring OpenEntityManagerInViewFilter 不会为您工作。它将实体管理器绑定到当前线程,以便 Spring 的事务管理代码可以重用它。问题是 spring 不处理服务 bean 的事务管理,因为这是由应用程序服务器处理的。应用服务器没有检测到spring绑定到线程的实体管理器,创建另一个;导致2个不同的实例。

要使其工作,您应该在 spring 中定义您的服务 bean,以便 spring 处理事务管理或使用 jboss seam(JBoss Seam: How to Open jpa/hibernate session in view

于 2013-05-10T17:48:24.023 回答
1

如果您确定视图过滤器中的打开会话正在工作,那么您可能需要查看service.retrieveParent(parentId);. 如果服务/dao 使用不同的持久性上下文来加载父实体,则 LazyInitializationException 将是有意义的。

于 2013-05-04T16:59:30.163 回答
1

我不熟悉 spring/jboss 设置,因此错过了一些东西。

我假设您的服务 bean 由 JBoss 服务器管理,而不是由 spring 管理。通过定义 LocalContainerEntityManagerFactoryBean 您实际上是在 Spring 中配置一个 EntityManagerFactory(非常类似于由应用程序服务器管理的那个)。视图过滤器中的打开会话由 Spring 管理的 EntityManager 注入,而它应该由应用程序服务器管理的实体管理器注入。

我认为以下 spring 配置将解决您的问题(不要忘记调整 jndi-name 属性)

<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/>

于 2013-05-09T22:32:11.403 回答
0

LazyInitializationException 意味着休眠尝试获取惰性集合/对象的数据,但会话已经关闭。

如果此行parent.getChildren().iterator().next().getName();抛出错误,则意味着这childrens是一个实际上没有有用数据的惰性集合,并且通过调用iterator().next()hibernate 尝试加载整个集合。

于 2013-05-07T14:50:20.060 回答