JVM/JPA

엔티티 그래프

kyoulho 2023. 7. 3. 14:40

엔티티를 조회할 때 연관된 엔티티를 함께 조회할 필요가 있으면 JPQL의 페치 조인을 사용한다. 그런데 페치 조인을 사용하면 같은 JPQL을 중복해서 작성하는 경우가 많다. 이는 JPQL이 엔티티를 조회할 뿐 아니라 연관된 엔티티를 함께 조회하는 기능도 제공하기 때문이다.

 JPA 2.1에 추가된 엔티티 그래프 기능을 사용하면 엔티티를 조회하는 시점에 함께 조회할 연관된 엔티티를 선택할 수 있다. 따라서 JPQL은 데이터를 조회하는 기능만 수행하면 되고 연관된 엔티티를 함께 조회하는 기능은 엔티티 그래프를 사용하면 된다. 그러므로 엔티티 그래프 기능을 적용하면 다음 JPQL만 사용하면 된다.

SELECT o FROM Order o WHERE o.status = ?

 

Named 엔티티 그래프

@NamedEntityGraph(name = "Order.withMember", attributeNodes = {
        @NamedAttributeNode("member")
})
@Entity
@Table(name = "ORDERS")
public class Order {
    @Id
    @GeneratedValue
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    private Member member;
}

// 사용
EntityGraph<?> graph = em.getEntityGraph("Order.withMember");
Map<String, Object> hints = new HashMap<>();
hints.put("javax.persistence.fetchgraph", graph);
Order order = em.find(Order.class, orderId, hints);

엔티티 그래프는 JPA의 힌트 기능을 사용해서 동작하는데 힌트의 키로 javax.persistence.fetchgraph를 사용하고 힌트의 값으로 찾아온 엔티티 그래프를 사용하면 된다.

 

subgraph

Order가 관리하지 않는 필드까지 함께 조회할 때 사용된다.

@NamedEntityGraph(name = "Order.withAll", attributeNodes = {
        @NamedAttributeNode("member"),
        @NamedAttributeNode(value = "orderItems", subgraph = "orderItems")
},
        subgraphs = @NamedSubgraph(name = "orderItems", attributeNodes = {
                @NamedAttributeNode("item")}))
@Table(name = "ORDERS")
@Entity @Getter @Setter
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    private Member member;

    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
    private List<OrderItem> orderItems = new ArrayList<>();
}


@Entity @Getter @Setter
public class OrderItem {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ORDER_ITEM_ID")
    private Long id;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "ITEM_ID")
    private Item item;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "ORDER_ID")
    private Order order;
}

 

JPQL에서 엔티티 그래프 사용

JPQL에서 엔티티 그래프를 사용할 경우 Order.member 처럼 필수 관계로 설정되어 있어도 외부 조인을 사용한다.

만약 SQL 내부 조인을 사용하려면 내부 조인을 명시하여 사용한다.

SELECT o From Order o JOIN FETCH o.member WHERE o.id = :orderId

 

 

동적 엔티티 그래프

EntityGraph<Order> graph = em.createEntityGraph(Order.class);
graph.addAttributeNodes("member");
        
Map<String, Object> hints = new HashMap<>();
hints.put("javax.persistence.fetchgraph", graph);
        
Order order = em.find(Order.class, orderId, hints);


// subgraph
EntityGraph<Order> graph = em.createEntityGraph(Order.class);
graph.addAttributeNodes("member");

Subgraph<Object> orderItems = graph.addSubgraph("orderItems");
orderItems.addAttributeNodes("item");

Map<String, Object> hints = new HashMap<>();
hints.put("javax.persistence.fetchgraph", graph);

Order findOrder = em.find(Order.class, orderId, hints);

 

엔티티 그래프 정리

ROOT 에서 시작

Order 엔티티를 조회하는데 Member부터 시작하는 엔티티 그래프를 사용하면 안 된다.

 

이미 로딩된 엔티티

영속성 컨텍스트에 해당 엔티티가 이미 로딩되어 있으면 엔티티 그래프를 적용해서 조회해도 다시 조회해도 처음 조회한 인스턴스가 반환된다.

 

fetchgraph, loadgraph의 차이

javax.persistence.fetchgraph 힌트는 엔티티 그래프에 선택한 속성만 함께 조회한다.

반면에 javax.persistence.loadgraph 속성은 글로벌 fetch 모드가 FetchType.EAGER로 설정된 연관관계도 포함해서 함께 조회한다.

'JVM > JPA' 카테고리의 다른 글

영속성 컨텍스트와 프록시  (0) 2023.07.03
JPA 예외 처리  (0) 2023.07.03
리스너  (0) 2023.07.02
@Converter  (0) 2023.07.02
컬렉션  (0) 2023.07.02