엔티티 그래프란?
엔티티를 조회할 때 연관된 엔티티들을 함께 조회하려면 글로벌 fetch 옵션을 FetchType.EAGER로 설정하거나 OneToMnay 관계에 경우는 JPQL에서 페치 조인을 사용하면 된다.
글로벌 fetch 옵션은 애플리케이션 전체에 영향을 주고 변경할 수 없는 단점이 있기 때문에 보통 LAZY를 사용하고, 엔티티를 조회할 때 연관된 엔티티를 함께 조회할 필요가 있으면 JPQL의 페치 조인을 사용한다.
그러나 JPQL을 사용할때는 다른곳에서 요청하는 데이터에 맞게 여러개를 생성해야 한다.
모두 같은 엔티티를 조회하지만 sql이 모두 다른 경우가 많을 경우 너무 많은 메소드를 생성해야 한다.
JPA 2.1에 추가된 엔티티 그래프 기능을 사용하면 엔티티를 조회하는 시점에 함께 조죄할 연관된 엔티티를 선택할 수 있다. 따라서 JPQL은 데이터를 조회하는 기능만 수행하면 되고 연관된 엔티티를 함께 조회하는 기능은 엔티티 그래프를 사용하면 된다.
한마디로 엔티티 조회시점에 연관된 엔티티들을 함께 조회하는 기능이다.
만약 Order 엔티티에 Member 엔티티와 함께 조회하는 엔티티 그래프를 설정하고 아래처럼 JPQL을 작성했을때 Member 엔티티까지 조회할 수 있는 것이다.
SELECT o FROM Order o WHERE o.id = ?
Named 엔티티 그래프
Named 엔티티 그래프는 정적으로 엔티티 그래프를 정의할 수 있다.
주문(Order)을 조회할 때 연관된 회원(Member)도 함께 조회하는 엔티티 그래프를 사용해보자.
Named 엔티티 그래프는 @NamedEntityGraph로 정의할 수 있다. 둘 이상 정의하려면 @NamedEntityGraphs를 사용하면 된다.
@NamedEntityGraph(
name = "Order.withMember", // 엔티티 그래프 이름 정의
attributeNodes = { @NamedAttributeNode("member") } // 함께 조죄할 속성 선택
);
@Entity
@Table(name = "ORDERS")
public class Order {
@Id
@GeneratedValue
@Column(name = "ORDER_ID")
private Long id;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "MEMBER_ID")
private Member member; // 주문 회원
}
Order.member가 지연 로딩으로 설정되어 있지만, 엔티티 그래프에서 함께 조회할 속성으로 member를 선택했기 때문에 member도 함께 조회할 수 있다.
Named 엔티티 그래프를 사용하려면 정의한 엔티티 그래프를 em.getEntityGraph("Order.withMember")를 통해서 찾아오면 된다.
EntityGraph graph = em.getEntityGraph("Order.withMember");
Map hints = new HashMap();
hints.put("javax.persistence.fetchgraph", graph);
Order order = em.find(Order.class, orderId, hints);
subgraph
이번에는 Order -> OrderItem -> Item까지 함께 조회해보자.
Order -> Orderiteam은 Order가 관리하는 필드지만 OrderItem -> Item은 Order가 관리하는 필드가 아니다.
이럴때 subgraph를 사용하면 된다.
@NamedEntityGraph(
name = "Order.withAll",
attributeNodes = {
@NamedAttributeNode("member"),
@NamedAttributeNode(value = "orderItems", subgraph = "orderItems")
},
subgraphs = @NamedSubgraph(name = "orderItems", attributeNodes = {
@NamedAttributeNode("item")
})
);
@Entity
@Table(name = "ORDERS")
public class Order {
@Id
@GeneratedValue
@Column(name = "ORDER_ID")
private Long id;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "MEMBER_ID")
private Member member; // 주문 회원
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<OrderItem> orderItems = new ArrayList<>();
}
@Entity
@Table(name = "ORDER_ITEM")
public class OrderItem {
@Id
@GeneratedValue
@Column(name = "ORDER_ITEM_ID")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "ITEM_ID")
private Item item; // 주문 상품
}
Order.withAll이라는 Named 엔티티 그래프를 정의했다. 이 엔티티 그래프는 Order ->Member, Order -> OrderItem, OrderItem -> Item의 객체 그래프를 함께 조회한다.
이때 OrderItem -> Item은 Order의 객체 그래프가 아니므로 subgraphs 속성으로 정의해야 한다.
Map hints = new HashMap();
hints.put("javax.persistence.fetchgraph", em.getEntityGraph("Order.withAll"));
Order order = em.find(Order.class, orderId, hints);
JPQL에서도 힌트만 추가해주면 엔티티 그래프를 사용할 수 있다.
List<Order> resultList = em.createQuery("SELECT o FROM Order o JOIN FETCH o.member WHERE o.id = :orderId", Order.class)
.setParameter("orderId", orderId)
.setHint("javax.persistence.fetchgraph", em.getEntityGraph("Order.withAll"))
.getResultList();
JPQL에서 엔티티 그래프를 사용하면 optional = false로 설정되어 있어도 내부 조인이 아닌 외부 조인을 사용한다.
만약 SQL 내부 조인을 사용하려면 SELECT o FROM Order o JOIN FETCH o.member WHERE o.id = :orderId로 명시해야 한다.
동적 엔티티 그래프
엔티티 그래프를 동적으로 구성하려면 createEntityGrapch() 메소드를 사용하면 된다.
EntityGraph graph = em.createEntityGraph(Order.class);
graph.addAttributeNodes("member");
Map hints = new HashMap();
hints.put("javax.persistence.fetchgraph", graph);
Order order = em.find(Order.class, orderId, hints);
subgraph 기능을 동적으로 구성할땐 아래처럼 작성한다.
EntityGraph graph = em.createEntityGraph(Order.class);
graph.addAttributeNodes("member");
Subgraph<OrderItem> orderItems = graph.addSubgraph("orderItems");
orderItems.addAttributeNodes("item");
Map hints = new HashMap();
hints.put("javax.persistence.fetchgraph", graph);
Order order = em.find(Order.class, orderId, hints);
fetchgraph, loadgraph의 차이
예제에는 javax.persistence.fetchgraph 힌트를 사용해서 엔티티 그래프를 조회했다.
이것은 엔티티 그래프에 선택한 속성만 함께 조회한다. 반면에 javax.persistence.loadgraph 속성은 엔티티 그래프에 선택한 속성뿐만 아니라 글로벌 fetch 모드가 FetchType.EAGER로 설정된 연관관계도 초함해서 함께 조회한다.
엔티티 그래프 사용 시 주의 사항
- 엔티티 그래프는 항상 조회하는 엔티티의 ROOT에서 시작해야 한다.
Order 엔티티를 조회하는데 Member부터 시작하는 엔티티 그래프르 사용하면 안된다. - 영속성 컨텍스트에 해당 엔티티가 이미 로딩되어 있으면 엔티티 그래프는 적용되지 않는다.
(아직 초기화되지 않은 프록시에는 엔티티 그래프가 적용됨)
'JPA' 카테고리의 다른 글
영속성 컨텍스트에 따른 엔티티 비교 (0) | 2022.01.24 |
---|---|
JPA 예외 처리 (0) | 2022.01.24 |
JPA 리스너를 이용한 공통 이벤트 처리 (0) | 2022.01.21 |
@Converter를 이용해 데이터를 변환해서 저장하고 가져오기 (0) | 2022.01.20 |
엔티티가 준영속 상태에서 생기는 문제점 (0) | 2022.01.20 |