6

I have the below mentioned Entity classes, when I execute my application I am getting the following exception. Some of the other similar questions didn't solve the problem.

WARNING: StandardWrapperValve[jersey-serlvet]: PWC1406: Servlet.service()
for servlet jersey-serlvet threw exception
org.hibernate.LazyInitializationException: failed to lazily initialize 
a collection of role: test.entity.Dept.empDeptno, no session
or session was closed
at org.hibernate.collection.internal.AbstractPersistentCollection.
throwLazyInitializationException(AbstractPersistentCollection.java:393)
       at    org.hibernate.collection.internal.AbstractPersistentCollection.
throwLazyInitializationExceptionIfNotConnected
(AbstractPersistentCollection.java:385)
    at org.hibernate.collection.internal.AbstractPersistentCollection.
initialize(AbstractPersistentCollection.java:378) 

How can I solve this issue?

Emp Entity

@Entity
@Table(name = "EMP", schema = "SCOTT"
)
@XmlRootElement
@NamedQueries({
    @NamedQuery(name = "Emp.findAllEmployees", query = "select e from Emp e left 
    join fetch e.deptNo order by e.empno desc")
})
public class Emp implements java.io.Serializable {
@Id
@Column(name = "EMPNO", unique = true, nullable = false, precision = 4,
scale = 0)
private short empno;
@ManyToOne
@JoinColumn(name = "DEPTNO", referencedColumnName = "DEPTNO")
private Dept deptNo;

Dept Entity

@Entity
@Table(name = "DEPT", schema = "SCOTT"
)
@XmlRootElement
public class Dept implements java.io.Serializable {
@Id
@Column(name = "DEPTNO", unique = true, nullable = false, precision = 2,
scale = 0)
private short deptno;
@OneToMany(fetch=FetchType.LAZY,mappedBy = "deptNo")
private Set<Emp> empDeptno;

DAOImpl

@Override
public List<Emp> findAllEmployees() {
  return getEntityManager().createNamedQuery("Emp.findAllEmployees",
 Emp.class).getResultList();
}

Jersey RESTful service

 @Component
 @Path("/employee")
 public class EmployeeRestService {

 @Autowired
 EmployeeService employeeService;

 @GET
 @Produces({MediaType.APPLICATION_JSON})
 public List<Emp> getEmployees() {
 List<Emp> emp = new ArrayList<Emp>();
 emp.addAll(getEmployeeService().findAllEmployees());
 return emp;
 }

Spring applicationContext.xml

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd"
>
    <!-- Data Source Declaration -->    
    <bean id="DataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiName" value="jdbc/scottDS"/>   
    </bean>

    <context:component-scan base-package="net.test" />
    <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
    <bean id="entityManagerFactory"
          class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="DataSource" />
        <property name="packagesToScan" value="net.test" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="false" />
                <property name="generateDdl" value="false" />
                <property name="databasePlatform" value="${jdbc.dialectClass}" />
            </bean>
        </property>
    </bean>
    <bean id="defaultLobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler" />  
    <!-- Transaction Config -->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
    <tx:annotation-driven transaction-manager="transactionManager"/>          
    <context:annotation-config/>
    <bean id="hibernateStatisticsMBean" class="org.hibernate.jmx.StatisticsService">
        <property name="statisticsEnabled" value="true" />
        <property name="sessionFactory" value="#{entityManagerFactory.sessionFactory}" />
    </bean>
</beans>
Jacob
  • 14,463
  • 65
  • 207
  • 320
  • have you tried accessing the collection while the session is still available? You may still receive a proxy object, so call a simple operation on it, like `size()` – kostja Nov 23 '13 at 12:10
  • @kostja I am calling method from RESTful service, I have posted my RESTful service and DAOImpl code snippet by editing my question. – Jacob Nov 23 '13 at 12:14
  • you might want to change the `FetchType` of the `empDetno` field to `EAGER`. A less clean solution would be to trigger loading of the `empDet` set in the `findAllEmployees` method by accessing it. – kostja Nov 23 '13 at 12:20
  • @kostja What if I would like to use `LAZY` as `fetchType`? And for the second solution, could you provide an example? Thanks – Jacob Nov 23 '13 at 12:22
  • Maybe this is a duplicate of http://stackoverflow.com/questions/5990005/jpa-lazyinitializationexception-when-returning-a-jaxb-object-through-a-webservic – isnot2bad Nov 23 '13 at 12:42
  • Possible duplicate of [How to solve the “failed to lazily initialize a collection of role” Hibernate exception](https://stackoverflow.com/questions/11746499/how-to-solve-the-failed-to-lazily-initialize-a-collection-of-role-hibernate-ex) – Martin Schröder Aug 08 '18 at 09:07

3 Answers3

9

I have resolved the issue by adding the following in 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>

courtesy here and here

Thanks

Jacob
  • 14,463
  • 65
  • 207
  • 320
5

The problem is that the scope of your database/JPA transaction only contains the service (which I assume is a stateless session bean) and does NOT contain the REST resource bean.

  1. Web server dispatches request to JAX-RS service
  2. JAX-RS service calls EJB Stateless Session Bean
  3. Transaction starts
  4. EJB Stateless Session Bean loads data from database (other beans might be involved)
  5. EJB Stateless Session Bean returns result
  6. Transaction ends
  7. JAX-RS service returns result
  8. JAX-RS Producer creates XML out of List<Emp> and accesses field empDeptno.

So when Jersey gets the list of Emp to produce XML out of it, the transaction has already been closed. When now the field empDeptNo is navigated, JPA tries to lazily load it, which fails as we're already outside a valid transaction/session.

You might try to extend the transaction scope to also contain your Jersey REST resource bean by making a stateless session bean out of it. Then it might be as follows:

  1. Web server dispatches request to JAX-RS service
  2. Transaction starts
  3. JAX-RS service calls EJB Stateless Session Bean
  4. EJB Stateless Session Bean loads data from database (other beans might be involved)
  5. EJB Stateless Session Bean returns result
  6. JAX-RS service returns result
  7. JAX-RS Producer creates XML out of List<Emp> and accesses field empDeptno.
  8. Transaction ends

I'm not 100% sure, it might also be that step 8 comes BEFORE step 7, so the transaction might be closed before the producer does its job. If that's the case, this solution is simply wrong...

But I think you should simply try it...

isnot2bad
  • 24,105
  • 2
  • 29
  • 50
  • You mean something like this? http://blog.bdoughan.com/2010/08/creating-restful-web-service-part-45.html – Jacob Nov 23 '13 at 13:09
  • By the way I am using Spring – Jacob Nov 23 '13 at 13:11
  • Yes exactly! But there they don't use lazy loading, so it's still not clear (to me), if it works because serialization is within transaction scope, or because all data to be serialized is already available. Maybe someone can clarify this! – isnot2bad Nov 23 '13 at 13:11
  • I have tried adding `@Transaction` in Rest Controller class, but it didn't help either. – Jacob Nov 23 '13 at 13:12
  • @Polppan I don't know spring very well, maybe this makes a difference. But the main principles should be the same. It's a question of transaction scope. – isnot2bad Nov 23 '13 at 13:13
  • I have edited my question with updated code of my REST Controller class. – Jacob Nov 23 '13 at 13:16
2

If you would like to continue using FetchType.LAZY but need access to the lazily loaded attributes for some queries, a portable solution would be to access the field and perform an operation on it while still within a transaction/session. I mention portability because AFAIK Hibernate offers at least one different approach to explicitly trigger loading that is not part of the JPA spec.

Adapting your code, this could look like this:

public List<Emp> findAllEmployees() {
  List<Emp> employees = getEntityManager().createNamedQuery("Emp.findAllEmployees",
    Emp.class).getResultList();

  //trigger loading of attributes
  for(Emp emp: employees){
    emp.getDeptNo().getEmpDetNo().size();
  }
  return employees;
}

EDIT: Another portable alternative would be to use fetch joins in the query. Your Emp.findAllEmployees query could look like this:

SELECT e FROM Emp e JOIN FETCH e.dept.empDetno

Make it a left join if you have Emps without departments and departments without empDetNo

kostja
  • 60,521
  • 48
  • 179
  • 224
  • I have a question, empDeptno is in Dept Entity, not in Emp Entity and second alternative I already have fetch join for my query. – Jacob Nov 23 '13 at 13:10
  • @Polppan right, sorry, read the question too hastily. I think you can extend the fetch join by navigation then. Have edited the answer accordingly. OTOH optimizing transaction demarcation like isnot2bad suggests is probably a better way. – kostja Nov 23 '13 at 13:17