본문 바로가기
JPA

[JPA] @ManyToOne, @OneToMany 를 활용하기

by kdohyeon (김대니) 2023. 2. 10.
반응형

이 어노테이션들은 언제 사용하는거지?

스프링에서 1:N 관계의 테이블을 엮어야 할 때 사용하게 된다.

예시

  • Order (주문) 엔티티와 OrderItem (주문 아이템) 엔티티는 @OneToMany, @ManyToOne 어노테이션을 활용하여 서로 1:N 관계로 묶여 있다.
    • 주문 1개에 N 개의 상품이 존재할 수 있다.

1:N 관계로 묶여 있는 Orders 와 OrderItems

엔티티 구현

Order (주문)

@Entity
@Table(name="orders")
public class Order {
    @Id
    @Column(name="order_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

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

OrderItem (주문아이템)

@Entity
@Table(name="order_items")
public class OrderItem {
    @Id
    @Column(name="order_item_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
    @JoinColumn(name = "order_id")
    private Order order;
}

조회하기

간단하게 JpaRepository 를 생성해두고 조회를 해보자.

public interface OrderJpaRepository extends JpaRepository<Order, Long> {}

@Test
void findAll() {
    var orders = orderJpaRepository.findAll();
}

findAll() 으로 조회하면 2개의 주문을 조회해온다. 이 때, 각 주문당 5개의 주문 아이템도 함께 묶여서 조회가 된다.

@OneToMany, @ManyToOne 속성

targetEntity

Optional) The entity class that is the target of the association. Optional only if the collection property is defined using Java generics. Must be specified otherwise. Defaults to the parameterized type of the collection when defined using generics

대상 클래스를 정의

cascade

(Optional) The operations that must be cascaded to the target of the association. Defaults to no operations being cascaded. When the target collection is a java.util.Map, the cascade element applies to the map value.

엔티티의 상태 변경에 대한 전이를 어떻게 할 것인지를 나타낸다. cascade 의 종류로는 ALL, PERSIST, MERGE, REMOVE, REFRESH, DETACH 가 존재하고 일반적으로는 ALL 을 주로 사용한다. 

주문과 주문 아이템의 예시로 살펴보자. 실제로 저장은 Order 객체만 수행하지만 cascade=ALL 으로 설정해두면 OrderItem 도 함께 DB 에 저장이 된다.

public class Main {
    public void sample() {
        OrderItem item1 = new OrderItem();
        OrderItem item2 = new OrderItem();
        
        Order order = new Order();
        order.addItems(item1);
        order.addItems(item2);
        
        orderRepository.save(order);
    }
}

fetch

(Optional) Whether the association should be lazily loaded or must be eagerly fetched. The EAGER strategy is a requirement on the persistence provider runtime that the associated entities must be eagerly fetched. The LAZY strategy is a hint to the persistence provider runtime.

묶여 있는 객체들을 어떻게 가져올지에 대한 전략을 적용할 수 있다. LAZY 와 EAGER 전략 2가지가 존재하는데, 보통 LAZY 를 많이 적용한다. EAGER (즉시 로딩) 전략은 객체를 조회할 때 부모-자식 객체를 모두 조회해서 가져오게 되는 것이고, LAZY (지연 로딩) 전략은 부모 객체만 가져오고 나중에 자식 객체를 사용할 때 조회하여 가져오는 전략이다.

mappedBy

The field that owns the relationship. Required unless the relationship is unidirectional.

참조할 클래스를 정의합니다.

orphanRemoval

(Optional) Whether to apply the remove operation to entities that have been removed from the relationship and to cascade the remove operation to those entities.

부모 엔티티와 관계가 끊긴 자식 엔티티를 자동으로 삭제해주는 역할을 한다.

주의할 점

Aggregate 범위를 잘 설정하자

논리적으로는 1:N 관계로 맵핑되어 있다고 해도 JPA 에서도 꼭 맞춰서 1:N 관계로 맵핑해줄 필요는 없다. JPA 에서 @ManyToOne, @OneToMany 로 두 테이블을 1:N 관계로 엮어 버리면 부모 테이블을 조회할 때, 항상 JOIN 쿼리가 실행되며 성능이 떨어질 수 있다. 따라서, 항상 같이 조회가 되어야 하는 테이블, 하나의 aggregate 로 묶여야 하는 테이블만 JPA 에서 묶는 것을 추천한다. 이번 예시에서는 주문과 주문 아이템을 예시로 기술하였지만 상품과 상품의 수정 이력 도메인에서도 함께 잘 사용된다. 

반응형

'JPA' 카테고리의 다른 글

[JPA] Spring 에서 JPA 사용하기  (0) 2023.02.11
[JPA] @EnableJpaAuditing 활용하기  (2) 2023.02.03
[JPA] @OrderBy  (0) 2023.02.03

댓글