JPA(Java Persistence API)에서 엔티티 간 관계를 맺고 있을 때, 참조값을 객체로 가지고 있는 엔티티를 조회하는 방식에 대해 설명합니다. 이는 데이터베이스 조회 성능과 애플리케이션의 동작에 중요한 영향을 미칩니다.
1. 즉시 로딩 (Eager Loading)
- 정의: 엔티티를 조회할 때 연관된 엔티티도 함께 조회합니다.
- 기본 적용: @ManyToOne, @OneToOne 관계
- 장점: 연관 엔티티를 즉시 사용할 수 있습니다.
- 단점: 불필요한 조인으로 인한 성능 저하가 발생할 수 있습니다.
2. 지연 로딩 (Lazy Loading)
- 정의: 연관된 엔티티를 실제로 사용할 때 조회합니다.
- 기본 적용: @OneToMany, @ManyToMany 관계
- 장점: 필요한 시점에 데이터를 로드하여 초기 조회 성능을 개선할 수 있습니다.
- 단점: 연관 엔티티 접근 시 추가 쿼리가 발생합니다.
3. 주요 개념
3.1 프록시 객체
- 지연 로딩 시 실제 엔티티 대신 사용되는 가짜 객체입니다.
- 실제 데이터가 필요할 때 데이터베이스에서 조회합니다.
3.2 N+1 문제
- 연관된 엔티티를 개별적으로 조회하여 발생하는 성능 문제입니다.
- 해결 방법: 페치 조인(Fetch Join), EntityGraph, BatchSize 등을 사용합니다.
3.3 영속성 컨텍스트
- 1차 캐시를 통해 이미 로드된 엔티티를 재사용합니다.
- 변경 감지(Dirty Checking)를 통해 엔티티의 상태 변화를 추적합니다.
4. 예제 코드
@Entity
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String orderNumber;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "customer_id")
private Customer customer; // getters and setters
}
@Entity
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "customer", fetch = FetchType.LAZY)
private List<Order> orders; // getters and setters
}
@Repository
public class OrderRepository extends JpaRepository<Order, Long> {
@Query("SELECT o FROM Order o JOIN FETCH o.customer WHERE o.id = :id")
Order findOrderWithCustomer(@Param("id") Long id);
}
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
public void processOrder(Long orderId) {
Order order = orderRepository.findById(orderId).orElseThrow();
System.out.println("Order Number: " + order.getOrderNumber()); // 지연 로딩 동작
System.out.println("Customer Name: " + order.getCustomer().getName());
}
public void processOrderWithFetch(Long orderId) {
Order order = orderRepository.findOrderWithCustomer(orderId);
System.out.println("Order Number: " + order.getOrderNumber());
System.out.println("Customer Name: " + order.getCustomer().getName());
}
}
5. 코드 설명
- Order와 Customer 엔티티는 다대일(N:1) 관계를 가짐
- @ManyToOne(fetch = FetchType.LAZY)로 지연 로딩 설정
- OrderRepository의 findOrderWithCustomer 메소드는 페치 조인 사용
- OrderService의 processOrder 메소드에서 지연 로딩 동작
- processOrderWithFetch 메소드는 페치 조인을 통해 N+1 문제 해결
6. 결론
JPA에서 엔티티 참조 조회 방식은 애플리케이션의 성능과 동작에 큰 영향을 미칩니다. 즉시 로딩과 지연 로딩의 특성을 이해하고, 상황에 맞는 적절한 전략을 선택하는 것이 중요함. 또한, N+1 문제와 같은 성능 이슈에 대비하여 페치 조인 등의 최적화 기법을 적절히 활용해야 함.
'데이터베이스 & ORM' 카테고리의 다른 글
VACUUM ANALYZE (1) | 2024.11.11 |
---|---|
PostgreSQL GIN (5) | 2024.11.10 |
QueryDSL 개요 (0) | 2024.07.25 |
Q DTO (0) | 2024.07.21 |
Entity, Repository, DTO, domain, service의 연관성과 개념 (0) | 2024.07.20 |