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

Kev*_*ave 5 java junit spring jpa struts2

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

这是我的测试课:

@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());

    }
}
Run Code Online (Sandbox Code Playgroud)

获取用户信息

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;
                }
            }
       }
       ...
       ...
    }
}
Run Code Online (Sandbox Code Playgroud)

User 和 UDetails 是实体。UDetalisManyToMany具有User延迟获取功能。所有实体都带有注释。

用户管理

public class UserMgmt {
    public User getUser(String userId) {
        return userDao.getUser(userId);
    }
}
Run Code Online (Sandbox Code Playgroud)

用户DAO

public class UserDAO extends AbstractJPAImpl{

    public User getUser(String userId) {
        User user = (User) getSession().get(User.class, userId);
        return user;
    }
}
Run Code Online (Sandbox Code Playgroud)

摘要JPAImpl

@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();
    }

    ....
}
Run Code Online (Sandbox Code Playgroud)

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"/>
Run Code Online (Sandbox Code Playgroud)

所有配置/上下文文件都加载良好。Struts.xml、jpacontext.xml、beans.xml等都被加载。

但我得到一个例外:

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: 
Run Code Online (Sandbox Code Playgroud)

在以下行:

 for(UDetails det : user.getUDetails())
Run Code Online (Sandbox Code Playgroud)

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

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

更新:更多信息

我在用OpenEntityManagerInViewFilter。我的 web.xml 下面

<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>
Run Code Online (Sandbox Code Playgroud)

更新:

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

@PersistenceContext(type=PersistenceContextType.EXTENDED)
Run Code Online (Sandbox Code Playgroud)

所以我猜想,事务正在关闭,但由于扩展上下文类型,实体可以在事务之外操作。但是,我无法像上面那样修改代码并永久保留它。所以我需要将其删除。

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

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

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

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

有什么帮助吗?

更新

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

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

Kev*_*ave 2

如果它对任何人有帮助,我无法让 @Transaction 工作。但我这样说:

 @PersistenceContext(type=PersistenceContextType.EXTENDED)
Run Code Online (Sandbox Code Playgroud)

现在可以了!

……

  • 你好。这里有同样的问题,但我使用的是 Spring Data JPA。如何使其与 JpaRepository 一起工作? (3认同)