Program Club

최대 절전 모드 : LazyInitializationException : 프록시를 초기화 할 수 없습니다.

proclub 2020. 11. 1. 19:02
반응형

최대 절전 모드 : LazyInitializationException : 프록시를 초기화 할 수 없습니다.


제가 당황하게 만드는 것이 있습니다. 기본 Hibernate DAO 구조를 구현하려고하는데 문제가 있습니다.

다음은 필수 코드입니다.

int startingCount = sfdao.count();
sfdao.create( sf );
SecurityFiling sf2 = sfdao.read( sf.getId() );
sfdao.delete( sf );
int endingCount = sfdao.count();

assertTrue( startingCount == endingCount );
assertTrue( sf.getId().longValue() == sf2.getId().longValue() );
assertTrue( sf.getSfSubmissionType().equals( sf2.getSfSubmissionType() ) );
assertTrue( sf.getSfTransactionNumber().equals( sf2.getSfTransactionNumber() ) );

sf의 값을 sf2의 해당 값과 비교하려는 세 번째 assertTrue에서 실패합니다. 예외는 다음과 같습니다.

org.hibernate.LazyInitializationException: could not initialize proxy - no Session
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:86)
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:140)
    at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190)
    at com.freightgate.domain.SecurityFiling_$$_javassist_7.getSfSubmissionType(SecurityFiling_$$_javassist_7.java)
    at com.freightgate.dao.SecurityFilingTest.test(SecurityFilingTest.java:73)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:40)

문제는 분리 된 개체의 컬렉션에 액세스하려고한다는 것입니다 . 현재 세션에 컬렉션에 액세스하기 전에 개체를 다시 연결해야합니다. 당신은 그것을 통해 할 수 있습니다

session.update(object);

lazy=false최대 절전 모드의 지연 초기화 기능을 버리기 때문에 사용 은 좋은 해결책이 아닙니다. 경우 lazy=false컬렉션 오브젝트가 요구되는 동시에 메모리에로드된다. 즉, 1000 개의 항목이있는 컬렉션이있는 경우 액세스 여부에 관계없이 모두 메모리에로드됩니다. 그리고 이것은 좋지 않습니다.

문제, 가능한 해결책 및 이러한 방식으로 구현되는 이유를 설명하는 기사를 읽으십시오 . 또한 세션 및 트랜잭션을 이해하려면 이 다른 기사를 읽어야 합니다 .


이것은 일반적으로 소유하는 Hibernate 세션이 이미 닫 혔음을 의미합니다. 다음 중 하나를 수행하여 수정할 수 있습니다.

  1. 이 문제를 일으키는 개체가 무엇이든 HibernateTemplate.initialize(object name)
  2. lazy=falsehbm 파일에서 사용하십시오 .

내 기사를 참조하십시오. LazyInitializationException이를 - - 저도 같은 문제를 겪고 여기에 드디어 해낸 대답 :
http://community.jboss.org/wiki/LazyInitializationExceptionovercome
게으른 = 거짓 대답없는 설정은 - 그것은 모두 한 번에 모든 것을로드하고 있다고 할 수 있습니다 반드시 좋은 것은 아닙니다. 예 :
1 개의 레코드 테이블 A 참조 :
5 개의 레코드 테이블 B 참조 :
25 개의 레코드 테이블 C 참조 :
125 개의 레코드 테이블 D
...
등. 이것은 잘못 될 수있는 한 가지 예일뿐입니다.
-팀 사빈


JPA 주석과 함께 최대 절전 모드를 사용하는 경우 유용합니다. 서비스 클래스에는 @PersistenceContext를 사용하는 엔티티 관리자에 대한 setter가 있어야합니다. 이것을 @PersistenceContext (type = PersistenceContextType.EXTENDED)로 변경하십시오. 그러면 어디서나 게으른 속성에 액세스 할 수 있습니다.


지연 로딩을 사용하는 경우 메소드에 주석을 추가해야합니다.

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) Stateless 세션 EJB 용


이 오류도 발생했습니다. 이 문제를 해결하기 위해 우리는 Hibernate 매핑 파일에 lazy = false추가했습니다 .

다른 클래스 B를로드하는 세션 내부에 클래스 A가있는 것 같습니다. 클래스 B의 데이터에 액세스하려고하지만이 클래스 B는 세션에서 분리됩니다.

이 클래스 B에 액세스하려면 클래스 A의 Hibernate 매핑 파일에 lazy = false 속성을 지정해야했습니다. 예를 들면

     <many-to-one name="classA" 
                 class="classB"
                 lazy="false">
        <column name="classb_id"
                sql-type="bigint(10)" 
                not-null="true"/>
    </many-to-one>  

좋아, 드디어 내가 어디를 잊었는지 알아 냈어. 나는 트랜잭션에서 각 DAO 메서드를 래핑해야한다는 잘못된 생각을하고있었습니다. 정말 잘못되었습니다! 나는 나의 교훈을 배웠다. 모든 DAO 메서드에서 모든 트랜잭션 코드를 가져 왔고 응용 프로그램 / 관리자 계층에서 엄격하게 트랜잭션을 설정했습니다. 이것은 내 모든 문제를 완전히 해결했습니다. 데이터는 필요에 따라 적절하게 지연로드되고, 커밋을 수행하면 래핑되고 닫힙니다.

인생은 훌륭합니다 ... :)


영향에 대해 알고 lazy=false있고 여전히 기본값으로 설정하려는 경우 (예 : 프로토 타이핑 목적으로) 다음 중 하나를 사용할 수 있습니다.

  • 당신이 XML 구성을 사용하는 경우 : 추가 default-lazy="false"사용자에 <hibernate-mapping>요소
  • 주석 구성을 사용하는 경우 : @Proxy(lazy=false)엔티티 클래스에 추가

DAO 만 세션을 사용하는 것 같습니다. 따라서 DAO 메서드를 호출 할 때마다 새 세션이 열리고 닫힙니다. 따라서 프로그램 실행은 다음과 같이 재개 될 수 있습니다.

// open a session, get the number of entity and close the session
int startingCount = sfdao.count();

// open a session, create a new entity and close the session
sfdao.create( sf );

// open a session, read an entity and close the session
SecurityFiling sf2 = sfdao.read( sf.getId() );

// open a session, delete an entity and close the session
sfdao.delete( sf );

etc...

기본적으로 엔터티의 수집 및 연결은 지연됩니다. 요청시 데이터베이스에서로드됩니다. 그러므로:

sf.getSfSubmissionType().equals( sf2.getSfSubmissionType() )

데이터베이스에서 새로드를 요청하고 엔티티로드와 관련된 세션이 이미 닫 혔기 때문에 예외가 발생합니다.

이 문제를 해결하는 방법에는 두 가지가 있습니다.

  • 모든 코드를 포함하는 세션을 만듭니다. 따라서 두 번째 세션을 열지 않도록 DAO 콘텐츠를 변경하는 것을 의미합니다.

  • 세션을 생성 한 다음 어설 션 전에 엔티티를이 세션에 업데이트 (다시 연결)합니다.

    session.update (객체);


Hibernate 세션을 수동으로 관리하는 경우 sessionFactory.getCurrentSession () 및 관련 문서를 여기에서 살펴볼 수 있습니다.

http://www.hibernate.org/hib_docs/v3/reference/en/html/architecture-current-session.html


I think Piko means in his response that there is the hbm file. I have a file called Tax.java. The mapping information are saved in the hbm (=hibernate mapping) file. In the class tag there is a property called lazy. Set that property to true. The following hbm example shows a way to set the lazy property to false.

` id ...'

If you are using Annotations instead look in the hibernate documenation. http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/

I hope that helped.


use Hibernate.initialize for lazy field


If you using Spring and JPA annotation, the simpliest way to avoid problem with session in lazy initialize is replaysing:

@PersistenceContext   

to

@PersistenceContext(type = PersistenceContextType.EXTENDED)

By default, all one-to-many and many-to-many associations are fetched lazily upon being accessed for the first time.

In your use case, you could overcome this issue by wrapping all DAO operations into one logical transaction:

transactionTemplate.execute(new TransactionCallback<Void>() {
    @Override
    public Void doInTransaction(TransactionStatus transactionStatus) {

        int startingCount = sfdao.count();

        sfdao.create( sf );

        SecurityFiling sf2 = sfdao.read( sf.getId() );

        sfdao.delete( sf );

        int endingCount = sfdao.count();

        assertTrue( startingCount == endingCount );
        assertTrue( sf.getId().longValue() == sf2.getId().longValue() );
        assertTrue( sf.getSfSubmissionType().equals( sf2.getSfSubmissionType() ) );
        assertTrue( sf.getSfTransactionNumber().equals( sf2.getSfTransactionNumber() ) );

        return null;
    }
});

Another option is to fetch all LAZY associations upon loading your entity, so that:

SecurityFiling sf2 = sfdao.read( sf.getId() );

should fetch the LAZY submissionType too:

select sf
from SecurityFiling sf
left join fetch.sf.submissionType

This way, you eagerly fetch all lazy properties and you can access them after the Session gets closed too.

You can fetch as many [one|many]-to-one associations and one "[one|many]-to-many" List associations (because of running a Cartesian Product).

To initialize multiple "[one|many]-to-many", you should use Hibernate.initialize(collection), right after loading your root entity.

참고URL : https://stackoverflow.com/questions/345705/hibernate-lazyinitializationexception-could-not-initialize-proxy

반응형