ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • JPA 양방향 연관관계, 왜 편의 메서드를 써야 할까?
    Jpa 2025. 7. 30. 00:55

     

    양방향 연관관계란?

    예를 들어, 하나의 주문(Order)은 여러 개의 주문 상품(OrderItem)을 가질 수 있습니다. 그리고 각 주문 상품은 하나의 주문에 속하게 됩니다. 이를 JPA로 표현하면 다음과 같습니다. 

    // Order.java
    @OneToMany(mappedBy = "orders", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<OrderItem> orderItems = new ArrayList<>();
    
    // OrderItem.java
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "order_id")
    private Order order;

     

    • Order → OrderItem: 주문이 가진 주문 항목들
    • OrderItem → Order: 이 항목이 속한 주문

    이 구조는 양방향 연관관계입니다. 양쪽에서 서로를 참조하고 있는 구조입니다. 

     

     

     

    연관관계 주의사항: 양방향이면 둘 다 설정해야함

     

    JPA에서는 실제 데이터베이스에 반영되는 외래 키의 주체를 ‘연관관계의 주인’이라고 합니다.
    양방향 관계에서는 반드시 한쪽이 주인이고, 다른 쪽은 거울(Mapped) 역할을 합니다.

    • 위 예제에서 OrderItem.orders가 연관관계의 주인입니다. (@ManyToOne 쪽)
    • Order.orderItems는 mappedBy 속성을 통해 단순히 반대 방향을 표현하는 비주인입니다.

    즉, DB의 order_id 컬럼은 OrderItem 객체의 orders 필드를 기준으로 설정됩니다.

     

     

     

    둘 중 하나만 설정하면 생기는 문제

    Order order = new Order();
    OrderItem item = new OrderItem();
    
    // Order → OrderItem만 설정
    order.getOrderItems().add(item);

     

    위 코드처럼 order 쪽 리스트에 item을 추가했다고 해서, 실제로 관계가 완성된 건 아닙니다.

    • OrderItem.order는 여전히 null이므로,
    • DB에 저장될 때 외래 키(order_id) 값도 null이 됩니다.
    • 이는 ConstraintViolationException 또는 관계가 끊어진 객체로 인한 NullPointerException의 원인이 됩니다.

     

     


     

     

     

    그래서 필요한 것이 "양방향 연관관계 편의 메서드"

    양쪽 관계를 한 번에 설정하는 메서드를 만들어 실수를 방지합니다.

    // Order.java
    public void addOrderItem(OrderItem item) {
        this.orderItems.add(item);   // order → item
        item.setOrders(this);        // item → order
    }

     

    • 자바 객체 간의 관계도 정확히 연결
    • JPA도 정상적으로 외래 키를 인식해 DB에 저장

     

    생성자 내부에서도 편의 메서드를 사용하자

    public Order(User user, int totalPrice, List<OrderItem> orderItems, OrderStatus orderStatus) {
        this.user = user;
        this.totalPrice = totalPrice;
        this.orderItems = new ArrayList<>();
        this.orderStatus = orderStatus;
    
        for (OrderItem item : orderItems) {
            addOrderItem(item); // 연관관계 편의 메서드
        }
    }

     

    이처럼 생성 시점에 양방향을 정확히 설정해두면, 추후 서비스 로직이나 테스트 코드에서 관계 오류를 방지할 수 있습니다.

     

     

     

    편의 메서드 없이 생기는 문제 요약

    사용 문제
    order.getOrderItems().add(item)만 호출 item.setOrders(order) 생략으로 관계 누락
    외래 키 누락 order_id가 null로 저장됨
    객체 그래프 불일치 order.getOrderItems()로 조회 불가능
    예외 발생 가능성 삭제·조회 시 NullPointerException, ConstraintViolationException

     

     


     

    실무에서는 어떻게 할까?

    • 연관관계의 주인이 아닌 쪽(대개 OneToMany)에 addXXX() 편의 메서드를 두는 것이 일반적입니다.
    • 도메인 모델 생성자 내부에서 편의 메서드를 사용하여 양쪽 관계를 동시에 설정합니다.
    • 한 쪽만 세팅하고 나머지를 잊는 실수를 방지합니다.

     

    결론

    • JPA에서는 연관관계의 주인만 DB에 반영되지만, 자바 객체 간의 관계까지 완전하게 만들려면 양쪽 모두 설정
    • 이를 자동화한 것이 양방향 연관관계 편의 메서드
    • 코드의 일관성, 디버깅 편의성, 예외 방지를 위해 반드시 사용

     

     

     

     

     

     

     

     

Designed by Tistory.