Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 301
Kevin Rave Avatar asked Oct 31 '22 14:10

Kevin Rave


1 Answers

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

 @PersistenceContext(type=PersistenceContextType.EXTENDED)

and it works now!

.....

like image 69
Kevin Rave Avatar answered Nov 15 '22 05:11

Kevin Rave