Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
648 views
in Technique[技术] by (71.8m points)

java - Keeping the session Open in JUnit/JPA/Hibernate/Struts and Spring integration test - No Session or session closed - LazyInitialization Exception

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

Here is my test class:

@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

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;
                }
            }
       }
       ...
       ...
    }
}

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

@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

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

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

But I get an exception:

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: 

at the following line:

 for(UDetails det : user.getUDetails())

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?

Update: More info

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>

Update:

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

@PersistenceContext(type=PersistenceContextType.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. Get the persistence context from spring application context and pass the parameter. Not sure if this is relevant and possible.

  2. 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.

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

Any help?

Update

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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

 @PersistenceContext(type=PersistenceContextType.EXTENDED)

and it works now!

.....


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...