Не удается заставить OpenEntityManagerInViewFilter работать в JBoss 6.1

Я пытаюсь добавить поведение открытого сеанса в поле зрения к существующему чистому приложению JPA. Использование Spring на уровне сервиса не вариант. Я хотел бы обернуть представление в Spring OpenEntityManagerInViewFilter, и не нужно изменять слой EJB.

Мне не повезло заставить OpenEntityManagerInViewFilter (Spring 3.2.2) работать в JBoss 6.1. Фильтр определенно вызывается, но я все еще получаю LazyInitializationException в представлении.

Фильтр и сессионный компонент используют другой экземпляр (и класс) EntityManager. Фильтр получает org.hibernate.ejb.EntityManagerImplв то время как сессионный компонент получает org.jboss.jpa.tx.TransactionScopedEntityManager, Я не уверен, что конфигурация Spring отвечает за это.

Вот соответствующий код / ​​конфиг:

война /WEB-INF/ классы /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 ответа

Решение

Я не думаю, что необходимо использовать пользовательский EntityManagerFactory для поиска EntityManagerFactory через JDNI, элемент должен позаботиться об этом.

Я немного подумал о вашей настройке, и я не думаю, что пружина OpenEntityManagerInViewFilter будет работать для вас. Он связывает менеджер сущностей с текущим потоком, так что код управления транзакциями Spring может использовать его повторно. Проблема в том, что Spring не обрабатывает управление транзакциями вашего компонента службы, так как он обрабатывается сервером приложений. Сервер приложений не обнаруживает менеджера сущностей, привязанного к потоку весной, и создает другой; в результате в 2 разных случаях.

Чтобы это работало, вы должны либо определить ваш (-ые) служебный (-ые) компонент (-ы) весной, чтобы Spring обрабатывал управление транзакциями, либо использовать шов jboss ( JBoss Seam: как открыть сессию jpa/hibernate)

Я не знаком с настройкой spring/jboss и поэтому пропустил некоторые вещи.

Я предполагаю, что ваш служебный компонент управляется сервером JBoss, а не весной. Определяя LocalContainerEntityManagerFactoryBean, вы фактически настраиваете 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/>

Если вы уверены, что фильтр открытого сеанса в представлении работает, вы можете взглянуть на демаркацию транзакции для service.retrieveParent(parentId);, LazyInitializationException будет иметь смысл, если служба / дао использует другой контекст постоянства для загрузки родительского объекта.

LazyInitializationException означает, что hibernate пытался получить данные отложенной коллекции / объекта, но сеанс уже был закрыт.

Если эта строка parent.getChildren().iterator().next().getName(); выдает ошибку, это означает, что childrens это ленивый сборник на самом деле без полезных данных, и путем вызова iterator().next() hibernate попытался загрузить всю коллекцию.

Другие вопросы по тегам