2013-11-23 7 views
6

Ho le classi di entità menzionate di seguito, quando eseguo la mia applicazione ottengo la seguente eccezione. Alcune delle altre domande simili non hanno risolto il problema.Hibernate org.hibernate.LazyInitializationException: impossibile inizializzare in modo lento una raccolta di ruoli:

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) 

Come posso risolvere questo problema?

Emp entità

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

Dipartimento entità

@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 servizio RESTful

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

Primavera 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> 
+0

avete provato l'accesso alla raccolta mentre la sessione è ancora disponibile? Potresti comunque ricevere un oggetto proxy, quindi richiamare una semplice operazione su di esso, come 'size()' – kostja

+0

@kostja Sto chiamando il metodo dal servizio RESTful, ho pubblicato il mio servizio RESTful e lo snippet di codice DAOImpl modificando la mia domanda. – user75ponic

+0

potresti voler cambiare 'FetchType' del campo' empDetno' in 'EAGER'. Una soluzione meno pulita sarebbe quella di attivare il caricamento dell'impostazione 'empDet' nel metodo' findAllEmployees' accedendovi. – kostja

risposta

9

ho risolto il problema aggiungendo il seguente nel 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> 

cortesia here e here

Grazie

2

Se si desidera continuare a utilizzare FetchType.LAZY ma hanno bisogno di accedere agli attributi pigramente caricati per alcune query, una soluzione portatile sarebbe per accedere al campo ed eseguire un'operazione su di esso mentre si è ancora all'interno di una transazione/sessione. Cito la portabilità perché AFAIK Hibernate offre almeno un approccio diverso per attivare esplicitamente il caricamento che non fa parte delle specifiche JPA.

Adattare il codice, questa potrebbe essere la seguente:

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: Un'altra alternativa portatile sarebbe quella di utilizzare prendere unisce nella query. Vostri criteri di Emp.findAllEmployees potrebbe essere la seguente:

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

fanno una sinistra unirsi se avete EMPS senza dipartimenti e reparti senza empDetNo

+0

Ho una domanda, empDeptno è in Entità di reparto , non in Emp Entity e la seconda alternativa ho già fetch join per la mia query. – user75ponic

+0

@Polppan, scusa, leggi la domanda troppo frettolosamente. Penso che tu possa estendere l'accesso al recupero tramite la navigazione. Ho modificato la risposta di conseguenza. OTOH che ottimizza la demarcazione delle transazioni come suggerisce isnot2bad è probabilmente un modo migliore. – kostja

+0

Grazie per l'aiuto, ho risolto il problema nella mia risposta. – user75ponic

5

Il problema è che la portata del vostro database/JPA transazione contiene solo il servizio (che presumo sia un bean di sessione stateless) e NON contiene il bean di risorse REST.

  1. Web server dispacci richiedere al servizio di JAX-RS
  2. servizio JAX-RS chiama EJB Stateless Session Bean
  3. transazione inizia
  4. EJB Stateless Session Bean carichi dati dal database (potrebbero essere coinvolti altri fagioli)
  5. EJB Stateless Session Bean restituisce il risultato
  6. transazione si conclude
  7. servizio JAX-RS restituisce il risultato
  8. JAX-RS Produttore crea XML fuori List<Emp> e accessi campo empDeptno.

Quindi quando Jersey ottiene l'elenco di Emp per produrre XML da esso, la transazione è già stata chiusa. Quando ora viene spostato il campo empDeptNo, JPA tenta di caricarlo pigramente, il che fallisce dato che siamo già al di fuori di una transazione/sessione valida.

Si potrebbe tentare di estendere l'ambito della transazione per contenere anche il proprio bean di risorse REST Jersey facendo fuoriuscire un bean di sessione stateless. Allora potrebbe essere la seguente:

  1. Web server dispacci richiedere al servizio di JAX-RS
  2. transazione inizia
  3. servizio JAX-RS chiama EJB Stateless Session Bean
  4. EJB Sean Bean Session carica i dati dal database (potrebbero essere coinvolti altri fagioli)
  5. EJB Stateless Session Bean restituisce il risultato
  6. servizio JAX-RS restituisce il risultato
  7. JAX-RS Produttore crea XML fuori List<Emp> e accessi campo empDeptno.
  8. transazione si conclude

Io non sono sicuro al 100%, ma potrebbe anche essere che il punto 8 viene prima fase 7, in modo dalla transazione potrebbe essere chiuso prima che il produttore fa il suo lavoro. Se questo è il caso, questa soluzione è semplicemente sbagliato ...

Ma penso che si dovrebbe semplicemente provare ...

+0

Vuoi dire qualcosa del genere? http://blog.bdoughan.com/2010/08/creating-restful-web-service-part-45.html – user75ponic

+0

A proposito, sto usando Spring – user75ponic

+0

Sì esattamente! Ma lì non usano il caricamento pigro, quindi non è ancora chiaro (per me), se funziona perché la serializzazione è nell'ambito della transazione, o perché tutti i dati da serializzare sono già disponibili. Forse qualcuno può chiarire questo! – isnot2bad

Problemi correlati