JPA 연관관계, 개념에서 실전까지 다시 정리하기

KUN·2025년 6월 22일

계기

이미 🙆‍♂️❤️🙅‍♀️ 짝사랑부터 연애까지 (JPA 연관관계) 에 대하여 작성한 글이 있다.
하지만, 이건 개념 만 잡아논거라 정확히 어떤 상황에서 어떻게 써야할지 감이 잡히지 않는다.

좀 더 확실하게 JPA 연관관계에 대해서 이해해보고자 작성한다.
정리하면, 어떤 상황이 가정되었을 경우 " 나는 그걸 연관관계로 만들 수 있을까? " 라는 질문을 스스로에게 던진 결과가 " 나는 만들지 못한다. " 였기에 다시 하기로 했다.


목표

  • 개념 훑기
  • 시나리오 정리
  • 각각 연관관계에 대하여 실습 진행

가볍게 개념만 훑기

OneToMany

하나의 엔티티가 여러 개의 다른 엔티티를 참조하는 관계
예: 한 회원이 여러 주문을 가질 수 있다

OneToOne

하나의 엔티티가 하나의 다른 엔티티만을 참조하는 관계
예: 회원과 회원 상세 정보가 1:1로 연결될 때

ManyToOne( Wikibooks )

여러 엔티티가 하나의 엔티티를 참조하는 관계
예: 여러 주문이 하나의 회원을 참조

( 상당히 복잡한 주제이므로, 위 소제목을 클릭하여 링크를 확인 )

ManyToMany

여러 엔티티가 서로 여러 엔티티를 참조하는 관계
예: 상품은 여러 태그를 가질 수 있고, 태그도 여러 상품에 붙을 수 있다


시나리오

시나리오 상황 가정

우리는 음료 전문 온라인 쇼핑몰을 운영하고 있다.
이 쇼핑몰에서는 단 세 가지 상품만 판매한다.
- 콜라(제로)
- 사이다(제로, 에너지)
- 이온음료(에너지)

------------[ 회원이라면 ? ]---------------
회원은 사이트에 로그인한 뒤, 원하는 음료를 고르고 주문할 수 있다.
한 번의 주문에서 여러 종류의 음료를 선택할 수 있고,
각 음료는 원하는 수량만큼 담을 수 있다.

예를 들어,
콜라 2개, 사이다 1개, 이온음료 5개
를 한 번에 주문했다. ( 이건 1개의 주문이다. )

그리고 " 아 맞다. 사이다 2개 더 사야돼. "
하면서, 사이다 2개를 추가로 주문할 수 있다. ( 이것도 1개의 주문이다. )
------------------------------------------

즉, 
1명의 회원은 <여러개의 주문>이 가능하며, 
주문안에 <여러개의 상품>이 들어갈 수 있다.
그리고 음료는 카테고리가 있어. ( 제로, 에너지 ) 으로 나뉘어져 있다면?

우리는 JPA 연관관계를 어떻게 작성해야할까?

상황이 발생했다.
우리는 이 상황에 따라서, 연관관계를 매핑할 줄 알아야한다.

시나리오 상황 정리

  1. 회원이 있어야 주문을 할 수 있다. ( 회원: Member, 주문: Order )
  2. 음료가 있어야 회원이 주문에 추가할 수 있다. ( Product )
  3. 하나의 주문 에는 여러개의 상품이 있다. ( OrderItem )
  4. 상품 별로 여러개의 카테고리 가 존재할 수 있다. ( Category )

순수 자바로 표현한다면?

public class Member {
    private Long id;
    private List<Order> orders;
}
↓1:N↓ 1 : N
public class Order {
    private Long id;
    private List<OrderItem> orderItems;
}
↓1:N↓ 1 : N
public class OrderItem {
    private Long id;
    private Product product;
    private int ea;
}
↓1:1↓ 1 : 1
public class Product {
    private Long id;
    private List<Category> categories;
}
↓N:M↓ N : M
enum Category {
    ZERO("제로"),
    ENERGY("에너지");

    private final String name;

    Category(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

( 모두 단방향으로 초기 설계를 했다. )

ERD 로 표현

관계형 데이터 베이스에서는 N : M 을 표현할 수가 없다.
그래서 중간테이블을 놔야 한다.

public class ProductCategory {
    private Long id;

    private Product product;
    private Category category;
} <그래서 자바코드에 다음과 같이 추가했다.>
인증샷인증샷


단방향에서 사용하려면?

예를 들어,
1. 특정 사용자의 ID 번호로 주문 번호를 전부 가져오고 싶다. ( R )
2. 특정 주문번호의 모든 상품을 가져오고 싶다.


1번 상황

문제점

찾을 수 없다.
즉, 특정 사용자는 찾을 수 있으나
특정 사용자의 주문 목록은 가져올 수 없다.

이유는 다음과 같다.
<단방향> 에서는 Member 가 Order이 있는지 모르기 때문이다.
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Getter
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
} 
이 부분에서 Member는 <Order>의 존재를 모른다. 
그렇기에 찾지 못하는 것이다.

해결법

단방향이므로 Order 에서 가져와야한다.

이런식으로 진행하면 해당 유저의 모든 주문목록을 가져올 수 있다.


2번 상황

마찬 가지로
< 특정 주문번호의 모든 상품을 가져오고 싶다. >

JOIN 을 사용하여 가져오게 한다.


배운점

  • JPA 연관관계는 객체지향적인 모델링을 가능하게 해준다.
  • 하지만 단방향 연관관계에서는 "연결된 쪽"에서만 탐색이 가능하다는 한계가 있다.
  • 계속해서 사용해봐야 감이 잡힌다.... 아직은 미숙하다.

실무에서는?

들은 바로는 실무에서는 QueryDSL을 주로 쓴다고한다. ( 전해들은 이야기로 )

그래서 다음에는
"왜 실무에서는 연관관계 매핑만으로는 부족한가?"
"왜 결국 QueryDSL이 선택되는가?" 를 알아보도록 하겠다.

profile
배우노라, 실험하노라, 기록하노라

0개의 댓글