본문 바로가기

Development/DB, JPA

Lazy Exception 과 Hibernate.initialize()

코딩을 하면서 트랜잭션과 관련된 문제는... 보고 당해도 늘 또 당한다...


하이버네이트에서 매핑된 객체를 가져올 때, OneToMany, ManyToMany는 LazyFetch를 기본전략으로 하고 있다.


따라서, 해당 객체를 부모객체가 자식객체를 가지고 올때는, 프락시 객체만 가지고 있으며, 실제 사용하는 시점 DBMS에 쿼리가 날아간다.


그런데 ! ! 


부모 객체를 가져오고 세션이 닫힌 뒤, 자식 객체를 콜하게 되면  org.hibernate.LazyInitializationException: could not...


익셉션이 발생 ! 


여러가지 해결 방법이 있는데,, 

1. Fetch 설정을 Eager로 해주는 가장 단순하고 위험한 방법이 있고,

2. DAO 에서 부모객체를 가져올때 Hibernate.Initialize(Object proxy)와 같은 방법으로 필요한 객체만 미리 Initialize 시킨 상태로 가져올 수도 있고,

3. Open Session In View 패턴으로 인터셉터나 필터를 적용하여, 뷰가 완전히 로딩될 때까지 세션을 열어두는 방법도 있다.


이 중에서 2의 방법을 오늘 사용했는데, 사용한 김에 이곳이 쓴다.. 

트랜잭션이 살아있는 서비스 메소드에서 

DAO로 부터 Member를 가져왔다.


1
2
3
4
5
6
@Service . . . 
 
public Member getMember(Long memberId){
    Member member = MemberDao.findMember(memberId);
    return member;
}
cs


이때 Member 는 ManyToOne 으로 Team 과 매핑되어 있고, FetchType.Lazy가 적용 되어있다면?


@Controller . . .에서 세션이 닫힌뒤 getMember(1).getTeamName(); 을 하게되면 LazyInitializationException 가 발생한다.


따라서 


1
2
3
4
5
6
public Member getMember(Long memberId){
    Member member = MemberDao.findMember(memberId);
    Hibernate.initialize(member.getTeam());
    Hibernate.initialize(member.getTeam().getTeamName()); // 오직 팀 이름만 필요하다면  이 방법도 가능.
    return member;    
}
cs


을 하면 Team 객체도 같이 초기화되어 Member를 리턴하기 때문에,  getMember(1).getTeamName(); 을 해도 예외가 발생하지 않고 팀 이름을 받아오게 된다.




LazyInitializeException을 해결하기 위한 방법은 많다. 다만 잘 찾아보고 알맞은 방법을 사용할것.. 자칫 잘못하면 1+N Problem등 과 같이, 시스템에 성능에 문제를 끼칠 수 있기 때문에..