복붙노트

[SPRING] JPA와 Hibernate에서의 LazyInitializationException

SPRING

JPA와 Hibernate에서의 LazyInitializationException

나는이 질문이 여러 번 여기에서 그리고 인터넷을 통해 물어 왔다는 것을 알고 있으며 그 답변들 중 많은 것들을 읽었지만 여전히이 문제를 해결할 적절한 방법을 이해하지 못한다. 나는 Spring MVC와 JPA를 실험하고 있는데, lazyInitializationException을 얻는다.

다음은 실험 할 코드 중 일부입니다.

@Repository
public class MyDAO {
    private static final Logger logger = LoggerFactory.getLogger(MyDAO.class);

    @PersistenceContext
    private EntityManager em;

    @Transactional
    public void logDOI() {
        DOI myDOI = em.find(DOI.class, Long.valueOf(1));

        // This line gives the expected output
        logger.info("Fetched DOI: " + myDOI.getDoiKey());

        // This line throws the LazyInitalizationException
        for(DOIMembership m : myDOI.getDoiMemberships()) {
            logger.info("Got DOI Membership id: " + m.getId());
        }
    }
}

내가 액세스하는 엔티티 :

@Entity
@Table(name="DOI")
public class DOI implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @SequenceGenerator(name="DOI_ID_GENERATOR", sequenceName="DOI_SEQ")
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="DOI_ID_GENERATOR")
    private long id;

    // Other properties omitted

    //bi-directional many-to-one association to DOIMembership
    @OneToMany(mappedBy="doi", fetch=FetchType.LAZY)
    private Set<DOIMembership> doiMemberships;

    public DOI() {
    }

    public long getId() {
        return this.id;
    }

    public void setId(long id) {
        this.id = id;
    }

    // Other Accessors Omitted

}

DOI에서 참조 된 개체

@Entity
@Table(name="DOI_MEMBERSHIP")
public class DOIMembership implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @SequenceGenerator(name="DOI_MEMBERSHIP_ID_GENERATOR", sequenceName="DOI_MEMBERSHIP_SEQ")
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="DOI_MEMBERSHIP_ID_GENERATOR")
    private long id;

    //bi-directional many-to-one association to DOI
    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="DOI_ID")
    private DOI doi;

    @Column(name="GROUP_ID")
    private BigDecimal groupId;

    @Column(name="DATA_SET")
    private BigDecimal dataSetId;

    public DOIMembership() {
    }

    public BigDecimal getGroupId() {
        return groupId;
    }

    public BigDecimal getDataSetId() {
        return dataSetId;
    }

    public void setDataSetId(BigDecimal dataSetId) {
        this.dataSetId = dataSetId;
    }

    public void setGroupId(BigDecimal groupId) {
        this.groupId = groupId;
    }

    public long getId() {
        return this.id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public DOI getDoi() {
        return this.doi;
    }

    public void setDoi(DOI doi) {
        this.doi = doi;
    }

}

스프링 MVC 컨트롤러 :

@Controller
@RequestMapping("/summary")
public class DOISummaryController {
    @Autowired
    MyDAO myDAO;

    @RequestMapping()
    public String DOISummary() {
        myDAO.logDOI();

        return "home";
    }
}

내 스프링 구성 :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:task="http://www.springframework.org/schema/task"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
        http://www.springframework.org/schema/task
        http://www.springframework.org/schema/task/spring-task-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.1.xsd">

    <!-- Root Context: defines shared resources visible to all other web components -->

    <context:property-placeholder location="WEB-INF/spring/root-context.properties, WEB-INF/spring/datasource-context.properties"  />

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName">
            <value>oracle.jdbc.driver.OracleDriver</value>
        </property>
        <property name="url">
            <value>${Url}</value>
        </property>
        <property name="username">
            <value>${Username}</value>
        </property>
        <property name="password">
            <value>${Password}</value>
        </property>
    </bean>

    <bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
        </property>
        <property name="packagesToScan" value="org.myorg.doi.domain" />
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.dialect">
                    org.hibernate.dialect.Oracle10gDialect
                </prop>
                <prop key="hibernate.max_fetch_depth">3</prop>
                <prop key="hibernate.jdbc.fetch_size">50</prop>
                <prop key="hibernate.jdbc.batch_size">10</prop>
                <prop key="hibernate.show_sql">true</prop>
            </props>
        </property>
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="emf" />
    </bean>

    <tx:annotation-driven transaction-manager="transactionManager" />

    <context:annotation-config />

    <task:annotation-driven />

    <context:component-scan base-package="org.myorg.doi" />

</beans>

그리고 요청 된 스택 추적 :

SEVERE: Servlet.service() for servlet [appServlet] in context with path [/DOI] threw exception [Request processing failed; nested exception is org.hibernate.LazyInitializationException: could not initialize proxy - no Session] with root cause
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
    at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:430)
    at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:121)
    at org.hibernate.collection.internal.PersistentSet.iterator(PersistentSet.java:180)
    at org.myorg.doi.dao.MyDAO.logDOI(MyDAO.java:27)
    at org.myorg.doi.web.DOISummaryController.DOISummary(DOISummaryController.java:29)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:213)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:126)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:96)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:617)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:578)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:225)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:927)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at com.springsource.insight.collection.tcserver.request.HttpRequestOperationCollectionValve.traceNextValve(HttpRequestOperationCollectionValve.java:116)
    at com.springsource.insight.collection.tcserver.request.HttpRequestOperationCollectionValve.invoke(HttpRequestOperationCollectionValve.java:98)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1001)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:585)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:680)

나도 알 수 있듯이 순수 JPA를 사용하려고하며 JPA 제공자로서 Hibernate만을 사용하려고한다.

이 예외는 엔티티에서 분리 된 세션 때문에 발생합니다. 그러나 우리가 현재 거래가 진행 중이면 logDOI 메소드가 @Transactional로 주석 처리 되었기 때문에 발생하지 않을 것이라고 생각했습니다.

물론 FetchType을 EAGER로 변경하면 모든 것이 완벽하게 작동하지만 그렇게 할 필요가없는 것 같습니다.

나는 또한 OpenEntityManagerInViewFilter에 대해 알고 있지만 @Transactional (또는 내가 알고 있지 못하는 다른 수단을 통해) 주석이 달린 DAO에서 내 엔티티에 대한 모든 액세스 권한을 유지해야하는 경우 사용할 필요가없는 것으로 보입니다.

나는 내가이 문제에 잘못 접근하고 있다고 생각하지만 올바른 접근법이 무엇인지 모른다. 느슨하게로드 된 속성을 효과적으로 사용하려면 어떻게해야합니까?

해결법

  1. ==============================

    1.Shailendra 덕분에 나는 거래를 자세히 살펴보기 시작했고 그 거래가 결코 시작되지 않았 음을 알았습니다. 그 정보로 저는 조사를하고 이것을 발견했습니다 : Spring @Transaction은 트랜잭션을 시작하지 않습니다. 내 servlet-context.xml 파일에 를 넣었습니다. 갑자기 logDOI에 대한 트랜잭션이 시작되었고 모든 것이 올바르게 작동했습니다. 더 이상 LazyInitializationException을 얻지 못했습니다. 나는 그것이 왜 효과가 있었는지 전혀 분명하지 않다. 그 정보는 인정 될 것입니다.

    Shailendra 덕분에 나는 거래를 자세히 살펴보기 시작했고 그 거래가 결코 시작되지 않았 음을 알았습니다. 그 정보로 저는 조사를하고 이것을 발견했습니다 : Spring @Transaction은 트랜잭션을 시작하지 않습니다. 내 servlet-context.xml 파일에 를 넣었습니다. 갑자기 logDOI에 대한 트랜잭션이 시작되었고 모든 것이 올바르게 작동했습니다. 더 이상 LazyInitializationException을 얻지 못했습니다. 나는 그것이 왜 효과가 있었는지 전혀 분명하지 않다. 그 정보는 인정 될 것입니다.

    최신 정보:

    나는 그것을 알아. 내 문제의 중요한 부분은 servlet-context.xml 파일에있었습니다. 이게 닮았 어.

    <?xml version="1.0" encoding="UTF-8"?>
    <beans:beans xmlns="http://www.springframework.org/schema/mvc"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:beans="http://www.springframework.org/schema/beans"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
    
        <!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
    
        <!-- Enables the Spring MVC @Controller programming model -->
        <annotation-driven />
    
        <!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
        <resources mapping="/resources/**" location="/resources/" />
    
        <!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
        <beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <beans:property name="prefix" value="/WEB-INF/views/" />
            <beans:property name="suffix" value=".jsp" />
        </beans:bean>
    
        <context:component-scan base-package="org.myapp.doi" />
    
    </beans:beans>
    

    가장 큰 문제점은 그 맥락에서였다 : 컴포넌트 스캔 라인. Spring MVC는 web.xml 파일의 contextConfigLocation 매개 변수로 정의 된 루트 애플리케이션 컨텍스트와 web.xml 파일의 DispatcherServlet에 정의 된 서블릿 컨텍스트에서 인스턴스화 된 두 개의 컨텍스트를 만듭니다. 서블릿 컨텍스트는 애플리케이션 컨텍스트를 볼 수 있지만 그 반대의 경우는 볼 수 없습니다. 이제 컨텍스트 : 서블릿 컨텍스트에서 정의 된 구성 요소 스캔이 전체 응용 프로그램 네임 스페이스를 검색하면 내 DAO가 서블릿 컨텍스트에서 인스턴스화되고 있습니다. 그러나 트랜잭션 주석 검사는 응용 프로그램 컨텍스트에서 수행 중이며 AOP 프록시 항목은 거기에서 수행 할 수 없습니다. 컨텍스트를 수정하기 만하면 서블릿 컨텍스트에서 component-scan을 수행하여 MVC 컨트롤러 ( 응용 프로그램 컨텍스트 및 트랜잭션에 대한 적절한 설정

  2. ==============================

    2.이 문제를 해결하는 가장 좋은 방법은 세션이 단일 거래로 인한 이유와시기를 먼저 이해하는 것입니다. 그리고 가장 좋은 방법은 log4j 설정에서 DEBUG 로깅 레벨을 하이버 네이트 (JPA 프로 바이더로서 최대 절전 모드를 사용함에 따라)로 설정하고 세션이 닫힌 곳을 추적하는 것입니다. 이것은 당신에게 명확한 그림을 줄 것입니다. 스택 추적은 기본 세션이 닫혔다는 것을 암시하지만 분명히 이유는 없습니다. 로그 된 관련 디버그 / 정보 메시지를 게시 할 수 있습니다.

    이 문제를 해결하는 가장 좋은 방법은 세션이 단일 거래로 인한 이유와시기를 먼저 이해하는 것입니다. 그리고 가장 좋은 방법은 log4j 설정에서 DEBUG 로깅 레벨을 하이버 네이트 (JPA 프로 바이더로서 최대 절전 모드를 사용함에 따라)로 설정하고 세션이 닫힌 곳을 추적하는 것입니다. 이것은 당신에게 명확한 그림을 줄 것입니다. 스택 추적은 기본 세션이 닫혔다는 것을 암시하지만 분명히 이유는 없습니다. 로그 된 관련 디버그 / 정보 메시지를 게시 할 수 있습니다.

    또한 트랜잭션 관리 인프라를 추적하기위한 스프링 프레임 워크에 대한 로깅을 설정할 수 있습니다

    로그는 기본 세션이 닫히고 트랜잭션이 커밋되었을 때 좋은 메시지를 제공합니다.

    예를 들어,

    opened session at timestamp: 13476520251
    ............
    ...........
    after transaction begin
    ..............
    
    select x,y,z from......
    ...............
    ...commit
    ..........
    ..flushing session
    ..after transaction completion
    ..closing session
    
  3. ==============================

    3.나는 또한이 문제를 가지고 있지만, 당신의 대답에 영감을 받아 해결했습니다. 여기 있습니다.

    나는 또한이 문제를 가지고 있지만, 당신의 대답에 영감을 받아 해결했습니다. 여기 있습니다.

    내 응용 프로그램은 Jpa 저장소를 사용하여 나머지 컨트롤러에서 데이터를 처리하려고 시도하는데 세션 오류가 발생하지 않습니다. 다음은 코드입니다.

    @RequestMapping(value = "/create", method = POST, consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Void> create(@RequestBody Map<String, Object> params) {
        Project project = Factory.project().build(params);
        project = repository.save(project);
        return ResponseEntity.ok().build();
    }
    

    이 게시물과이 게시물에 따르면 서블릿 컨텍스트의 Bean은 응용 프로그램 컨텍스트에서 Bean을 참조 할 수 있다는 것을 알고 있습니다. 따라서 TransactionManager는이 Rest Controller Bean에 액세스 할 수 없으므로이 오류가 발생합니다.

    솔루션 - 나머지 컨트롤러와 저장소 사이에 중간 계층 Bean 응용 프로그램 컨텍스트를 만들고 해당 코드를 캡슐화합니다. 나는 그것을 시도, 잘 작동합니다. 나중에 코드를 업데이트하십시오.

  4. ==============================

    4.먼저, @Autowired로 설정된 모든 의존성에 대해, 사용할 Spring Bean foreach DAO를 선언해야하며 런타임에 삽입 될 것이다.

    먼저, @Autowired로 설정된 모든 의존성에 대해, 사용할 Spring Bean foreach DAO를 선언해야하며 런타임에 삽입 될 것이다.

    어쩌면이 DAO는 다음과 같은 sessionFactory 레퍼런스를 가질 필요가있다.

    <!-- a dao in which you inject your Hibernate SessionFactory bean by its own id -->
    <bean id="userDAO" class="com.enterprise.model.dao.UserDAO">
        <property name="sessionFactory">
            <ref bean="sessionFactoryId" />
        </property>
    </bean>
    
    <!-- activate the @Repository annotation -->
    <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
    

    두 번째로, 스프링 XML 설정 안에 이것을 추가하는 것을 잊어 버린 것일 수 있습니다.

    <!-- Add JPA support -->
    <bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="loadTimeWeaver">
            <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
        </property>
    </bean>
    
    <!-- Add Transaction support -->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="emf"/>
    </bean>
    

    셋째, 인터셉터 항목 :

    <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
        <property name="interceptors">
            <list>
                <ref bean="openEntityManagerInViewInterceptor"/>
            </list>
        </property>
    </bean>
    
    <bean id="openEntityManagerInViewInterceptor"
          class="org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    
  5. from https://stackoverflow.com/questions/12428085/lazyinitializationexception-in-jpa-and-hibernate by cc-by-sa and MIT license