즉시 로딩은 엔티티를 로딩할 때 연관된 엔티티도 함께 조회하는 방식입니다. 필요하지 않은 데이터도 미리 로딩되기 때문에 메모리를 많이 사용하지만, 반복 쿼리 발생을 방지할 수 있습니다.
@OneToOne(fetch = FetchType.EAGER)
또는 @ManyToOne(fetch = FetchType.EAGER)
예시 코드:
@OneToOne(fetch = FetchType.EAGER)
private Address address;
지연 로딩은 실제로 엔티티에 접근할 때 데이터를 로딩하는 방식입니다. 메모리를 절약하고 성능을 최적화할 수 있지만, 트랜잭션이 끝나면 지연 로딩된 필드에 접근할 때 예외가 발생할 수 있습니다.
@OneToMany(fetch = FetchType.LAZY)
또는 @ManyToOne(fetch = FetchType.LAZY)
LazyInitializationException
발생 가능예시 코드:
@OneToMany(mappedBy = "member", fetch = FetchType.LAZY)
private List<Order> orders;
지연 로딩을 안전하게 수행하려면 트랜잭션 범위 내에서 영속성 컨텍스트가 열려 있어야 합니다. 서비스 계층에서 @Transactional
어노테이션을 사용하여 트랜잭션 범위 내에서 지연 로딩된 엔티티에 접근하는 방식이 일반적입니다.
@Transactional
어노테이션 사용 예시아래는 서비스 계층에서 @Transactional
을 적용하여 지연 로딩을 안전하게 수행하는 예시입니다.
@Service
public class MemberService {
@Autowired
private EntityManager em;
@Transactional // 트랜잭션이 시작되어 영속성 컨텍스트가 열림
public Member getMemberWithOrders(Long memberId) {
// Member 엔티티 조회 (이때 orders 필드는 로딩되지 않음)
Member member = em.find(Member.class, memberId);
// 트랜잭션 범위 내에서 지연 로딩이 발생하므로 LazyInitializationException이 발생하지 않음
System.out.println(member.getOrders().size()); // 이때 orders에 대한 쿼리 실행
return member;
}
}
트랜잭션이 종료된 후 지연 로딩된 필드에 접근하려 하면 LazyInitializationException
이 발생합니다. 아래와 같은 상황에서 예외가 발생할 수 있습니다.
public void someMethod() {
Member member = memberService.getMemberWithOrders(1L); // 트랜잭션 없이 호출
System.out.println(member.getOrders().size()); // LazyInitializationException 발생
}
지연 로딩은 성능 최적화를 위해 중요하지만, 트랜잭션 범위 내에서 안전하게 사용하지 않으면 LazyInitializationException
이 발생할 수 있습니다. @Transactional
을 통해 트랜잭션을 관리하고, 필요에 따라 즉시 로딩을 선택하는 등 로딩 전략을 상황에 맞게 최적화하는 것이 중요합니다.