2013-11-23 3 views
6

У меня есть следующие классы Entity, когда я выполняю мое приложение, я получаю следующее исключение. Некоторые другие подобные вопросы не решают проблему.Hibernate org.hibernate.LazyInitializationException: не удалось лениво инициализировать коллекцию роли:

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) 

Как я могу решить эту проблему?

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; 

Отдел 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(); 
} 

Джерси 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; 
} 

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

вы пытались получить доступ к коллекции во время сеанса все еще доступен? Вы все еще можете получить прокси-объект, поэтому вызовите на него простую операцию, например 'size()' – kostja

+0

@kostja. Я вызываю метод из службы RESTful, я опубликовал мою службу RESTful и фрагмент кода DAOImpl, отредактировав мой вопрос. – user75ponic

+0

, вы можете захотеть изменить 'FetchType' поля' empDetno' на 'EAGER'. Менее чистым решением было бы инициировать загрузку набора 'empDet' в метод' findAllEmployees', обратившись к нему. – kostja

ответ

9

я решил проблему, добавив следующую строку в Интернете.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> 

любезно here и here

Благодаря

2

Если вы хотите продолжать использовать FetchType.LAZY но нужен доступ к лениво нагруженным атрибутам для некоторых запросов, портативное решение будет для доступа к полю и выполнения операции над ним, пока он еще находится в транзакции/сеансе. Я упоминаю о переносимости, потому что AFAIK Hibernate предлагает по крайней мере один другой подход для явного запуска загрузки, который не является частью спецификации JPA.

Адаптация коды, это может выглядеть следующим образом:

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: Еще один портативной альтернативой будет использовать выборки соединений в запросе. Ваш Emp.findAllEmployees запрос может выглядеть следующим образом:

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

Сделать это левое соединение, если у вас есть EMPS без отделов и ведомств без empDetNo

+0

У меня вопрос, empDeptno is in Dept Entity , а не в Emp Entity и второй альтернативе, у меня уже есть выборка для моего запроса. – user75ponic

+0

@ Полппан справа, извините, слишком быстро прочитайте вопрос. Я думаю, что вы можете расширить соединение для получения путем навигации. Соответственно отредактировали ответ. OTOH, оптимизирующий демаркацию транзакций, например isnot2bad, вероятно, является лучшим способом. – kostja

+0

Спасибо за помощь, я решил проблему в своем ответе. – user75ponic

5

Проблема в том, что сфера вашей базы данных/JPA транзакции содержит только сервис (который, как я полагаю, является сессионным компонентом без состояния) и НЕ содержит компонент ресурса REST.

  1. Веб-сервер депеши запрос в службу JAX-RS
  2. сервис JAX-RS называет EJB сеансный компонент
  3. Сделка начинается
  4. EJB сессионный Bean нагрузки данные из базы данных (могут использоваться другие компоненты)
  5. EJB без гражданства Session Bean возвращает результат
  6. сделка заканчивается
  7. службы JAX-RS возвращает результат
  8. JAX-RS Производитель создает XML из List<Emp> и обращается поле empDeptno.

Итак, когда Джерси получает список Emp, чтобы произвести XML из него, транзакция уже закрыта. Когда теперь перемещается поле empDeptNo, JPA пытается лениво загрузить его, что не удается, поскольку мы уже находимся за пределами действительной транзакции/сессии.

Возможно, вы попытаетесь расширить область транзакций, чтобы также содержать ресурс ресурса Джерси REST, создав из него неактивный сеансовый bean-компонент. Тогда это может выглядеть следующим образом:

  1. Веб-сервер депеши запрос в службу JAX-RS
  2. Сделка начинается
  3. JAX-RS сервис называет EJB сеансный компонент
  4. EJB Безстоящий сеанс Bean загружает данные из базы данных (могут использоваться другие компоненты)
  5. EJB Stateless Session Bean возвращает результат
  6. службы JAX-RS возвращает результат
  7. JAX-RS Производитель создает XML из List<Emp> и обращается поле empDeptno.
  8. сделка заканчивается

Я не уверен на 100%, это может быть также, что шаг 8 предшествует шаг 7, поэтому сделка может быть закрыта до того, как производитель делает свою работу. Если это так, то это решение просто неправильно ...

Но я думаю, вы должны просто попробовать ...

+0

Вы имеете в виду что-то вроде этого? http://blog.bdoughan.com/2010/08/creating-restful-web-service-part-45.html – user75ponic

+0

Кстати я использую Spring – user75ponic

+0

Да точно! Но там они не используют ленивую загрузку, поэтому мне все еще не ясно (мне), если она работает, потому что сериализация находится в пределах транзакции или потому, что все данные, которые будут сериализованы, уже доступны. Может быть, кто-то может это прояснить! – isnot2bad

Смежные вопросы