Не удается заставить 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 попытался загрузить всю коллекцию.