保持会话在JUnit / JPA / Hibernate / Struts和Spring集成测试中打开 - 没有会话或会话关闭 - LazyInitialization异常

保持会话在JUnit / JPA / Hibernate / Struts和Spring集成测试中打开 - 没有会话或会话关闭 -  LazyInitialization异常

问题描述:

我的应用程序使用Struts2(mvc),Spring(依赖注入),JPA和Hibernate,JUnit以及struts2-junit插件和struts2 spring插件。

My application uses Struts2(mvc), Spring (Dependency Injection), JPA with Hibernate, JUnit along with struts2-junit plugin and struts2 spring plugin.

这是我的测试类:

@RunWith(SpringJUnit4ClassRunner.class)
public class CustomerSearchIntegrationTest extends StrutsSpringTestCase {

    @Test
    @Transactional
    public void testGetActionProxy() throws Exception {

        ActionProxy proxy;
        String result;

        ActionMapping mapping = getActionMapping("userInfo");
        assertNotNull(mapping);

        ..... // Some JUnit init code..

        ActionProxy proxy = getActionProxy("userInfo");
        UserInfo user = (UserInfo) proxy.getAction();
        result = proxy.execute();

        assertEquals("details", result);
        System.out.prinltn("Username:" + user.getFirstName());

    }
}

GetUserInfo

GetUserInfo

public class UserInfo extends ActionSupport {
     User user; // Entity
     UDetails details; // Entity

     public String getUserDetails() { //Action method
       user = userMgmt.getUser(usrId);
       if (user != null ) {
            for(UDetails det : user.getUDetails()) { // user.getUDetails() there is where exception gets thrown.
                if(det.getStreet().equals(street)){
                    details = det;
                    break;
                }
            }
       }
       ...
       ...
    }
}

用户和UDetails是实体。 UDetalis是 ManyToMany ,带有用户并且已经提取了Lazily。所有实体都有注释。

User and UDetails are entities. UDetalis is ManyToMany with User and Lazily fetched. All entities are annotated.

UserMgmt

public class UserMgmt {
    public User getUser(String userId) {
        return userDao.getUser(userId);
    }
}

UserDAO

public class UserDAO extends AbstractJPAImpl{

    public User getUser(String userId) {
        User user = (User) getSession().get(User.class, userId);
        return user;
    }
}

AbstractJPAImpl

AbstractJPAImpl

@Transactional
public abstract class AbstractJPAImpl {

    private EntityManager em;

    @PersistenceContext
    protected void setEntityManager(EntityManager em) {
        this.em = em;
    }

    @Transactional
    protected Session getSession() {
        return (Session) em.getDelegate();
    }

    ....
}

jpaContext.xml

jpaContext.xml

<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
  // other config stuff
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>

<tx:annotation-driven transaction-manager="transactionManager"/>

所有配置/上下文文件都正常加载。所有的struts.xml,jpacontext.xml,beans.xml等都已加载。

All configuration/context files are loading fine. Struts.xml, jpacontext.xml, beans.xml, etc. all are loaded.

但我得到一个例外:

failed to lazily initialize a collection of role: com.my.data.User.udetails, no session or session was closed

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: 

位于以下行:

 for(UDetails det : user.getUDetails())

显然,它试图懒洋洋地加载 UDetails ,然后抛出异常。
但是,此应用程序在AppServer(WebSphere)中部署时可以正常工作。

Obviously, its trying to load UDetails lazily, then exception is throwing. However, this application works fine when deployed in a AppServer (WebSphere).

我能做错什么?如何保持会话开放?

What could I be doing wrong? How do I keep session open?

更新更多信息

我是使用 OpenEntityManagerInViewFilter 。我的web.xml下面

I am using OpenEntityManagerInViewFilter. My web.xml below

<filter>
    <filter-name>OpenEntityManagerInViewFilter</filter-name>
    <filter-class>
        org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>OpenEntityManagerInViewFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

更新:

如果我将参数传递给 @PersistenceContext 注释,如果将参数传递给下面,那么这最终会起作用:

This finally works, if I pass the parameter to @PersistenceContext annotation like below:

@PersistenceContext(type=PersistenceContextType.EXTENDED)

所以我想,这个交易正在关闭,但由于EXTENDED上下文类型,实体可以在事务之外操作。但是,我不能像上面那样修改代码并永久保留它。所以我需要删除它。

So I guess, the transaction is getting closed, but the entities are operable outside of transaction, because of the EXTENDED context type. However, I can't modify the code like above and leave it permanently. So I need to remove it.

所以我想,我有这些选项,但不确定这些是否可行以及如何:

So I guess, I have these options, but not sure if these are doable and how:


  1. 从spring应用程序上下文中获取持久性上下文并传递参数。不确定这是否相关且可能。

  1. Get the persistence context from spring application context and pass the parameter. Not sure if this is relevant and possible.

从应用程序上下文获取会话/实体管理器并添加另一层事务。这样,会话在两个事务中运行。一个是从我的测试代码开始的,另一个是在现有代码中,它会自动关闭,而我的测试代码完成执行后仍保持打开状态。

Get the session/entity manager from application context and add another layer of transaction. That ways, the the session runs in two transactions. One is started from my testing code, and another one is in the existing code, which is automatically getting closed, while mine remains open until my test code completes execution.

对于第二个,我尝试使用 @Transactional 来注释该方法。但那没用。不知道为什么。

For the 2nd one, I tried annotatting the method with @Transactional. But that did not work. Not sure why.

任何帮助?

更新

看起来,struts-junit插件不会加载/读取web.xml,它具有 OpenEntityManagerInViewFilter 。因此它没有加载。

Looks like, struts-junit plugin does NOT load/read web.xml, which has the OpenEntityManagerInViewFilter. So it did not get loaded.

还有其他解决方法或设置此过滤器吗?

Any other work around or setup this filter?

如果它对任何人有帮助,我无法让@Transaction工作。但我把它放在:

If it helps anyone, I couldn't get the @Transaction to work. But I put this:

 @PersistenceContext(type=PersistenaceContextType.EXTENDED)

现在可以使用了!