/////
Search
Duplicate
4️⃣

애그리거트 로딩 전략

: JPA 매핑을 설정할 때, 항상 기억해야 하는 점은 애그리거트에 속한 객체가 모두 모여야 완전한 하나가 된다는 것이다.
Product product = productRepository.findById(id);
Java
복사
: 위 함수의 경우 애그리거트를 완전한 상태가 되도록 하려면 애그리거트 루트에서 연관 매핑의 조회 방식을 즉시 로딩(FetchType.EAGER)로 설정하면 된다.
: 다음 같이 컬렉션이나 @Entity에 대한 매핑의 fetch 속성을 즉시 로딩으로 설정하면 EntityManager#find() 메서드로 애그리거트 루트를 구할 때 연관된 구성요소를 DB에서 함께 읽어온다.
: 즉시 로딩 방식을 사용할 경우 애그리거트 루트를 로딩하는 시점에 애그리거트에 속한 모든 객체를 함께 로딩할 수 있다는 장점이 있지만 이는 항상 좋은 방법인 것은 아니다.
: 예를 들어 Product 애그리거트 루트가 @Entity로 구현한 Image와 @Embeddable로 구현한 Option 목록을 가지고 있다고 해보자.
@Entity @Table(name = "product") public class Product { ... @OneToMany( cascade = {CascadeType.PERSIST, CascadeType.REMOVE}, orphandRemoval = true, fetch = FetchType.EAGER) @JoinColumn(name = "product_id") @OrderColumn(name = "list_idx") private List<Option> options = new ArrayList<>(); @ElementCollection(fetch = FetchType.EAGER) @CollectionTable(name = "product_option", joinColumns = @joinColumn(name = "product_id")) @OrderColumn(name = "list_idx) private List<Option> options = new ArrayList<>(); ... }
Java
복사
: 이 매핑을 사용할 때 EntityManager#find() 메서드로 Product를 조회하면 하이버네이트는 Product를 위한 테이블, Image를 위한 테이블, Option을 위한 테이블을 조인한 쿼리를 실행한다.
: 이는 불필요한 데이터까지 모두 가져오므로 성능 이슈가 발생할 수 있다.
: 애그리거트는 개념적으로 하나여야 한다. 하지만, 루트 엔티티를 로딩하는 시점에 애그리거트에 속한 객체를 모두 로딩해야 하는 것은 아니다.
: 애그리거트가 완전해야 하는 이유는 두가지 정도이다.
상태를 변경하는 기능을 실행할때 애그리거트 상태가 완전해야 한다.
표현 영역에서 애그리거트의 상태 정보를 보여줄 때 필요하다.
: 상태 변경 기능을 실행하기 위해 조회 시점에 즉시 로딩을 이용해서 애그리거트를 완전한 상태로 로딩할 필 요는 없다.
: JPA는 트랜잭션 범위 내에서 지연 로딩을 허용하기 때문에 다음 코드처럼 실제로 상태를 변경하는 시점에 필요한 구성요소만 로딩해도 문제가 되지 않는다.
@Transactional public void revmoeoptions(ProductId id, int optIdxToBeDeleted) { //Product를 로딩/ 컬렉션은 지연 로딩으로 설정했다면 Option은 로딩되지 않음 Product product = productRepository.findByid(id); // 트랜잭션 범위이므로 지연 로딩으로 설정한 연관 로딩 가능 product.removeOption(optIdxToBeDeleted); }
Java
복사
@Entity public class Product { @ElementCollection(fetch = FetchType.LAZY) @CollectionTable(name = "product_option", joinColumns = @JoinColumn(name = "product_id")) @OrderColumn(name = "list_idx") private List<Option> options = new ArrayList<>(); public void removeOption(int optIdx) { //실제 컬렉션에 접근할 때 로딩 this.options.remove(optIdx); } }
Java
복사
: 일반적으로 상태를 변경하기 보다는 조회하는 빈도 수가 높다. 이런 이유로 애그리거트 내의 모든 연관을 즉시 로딩으로 설정할 필요는 없다.
: 물론, 지연 로딩은 즉시 로딩보다 쿼리 실행 횟수가 많아질 가능성이 더 높다. 따라서, 무조건 즉시 로딩이나 지연 로딩으로만 설정하기보다는 애그리거트에 맞게 즉시 로딩과 지연 로딩을 선택해야 한다.