본문 바로가기
Framework and Tool/JPA

JPA - 연관관계 맵핑 - 연관관계 맵핑

by ocwokocw 2021. 7. 2.

- 참조: 자바 ORM 표준 JPA 프로그래밍

- 외래키에서 참조로의 변환

지난번 요구사항을 Entity 로 변환하는 과정에서 아래 다이어그램과 같이 속성으로 외래키를 그대로 냅두었었다.

연관관계 맵핑을 배웠으니 이 외래키들을 참조로 변환할 수 있다. 연관관계를 더 명확히 보기 위해서 해당 필드에도 다중성을 표시하였다.

아래 설명은 위의 다이어그램에서 클래스간의 관계를 파악한것이다.

  • Member - Order: 멤버는 주문을 1건도 하지 않을 수도, 여러건을 할 수도 있다. 1 : N 의 관계이다. Member 와 Order 는 서로간을 참조하는 양방향 연관관계이다. 외래키는 Order 에서 MEMBER_ID 를 가져야 한다. 앞에서 설명했듯이 Member : Order = 1 : N 이므로 N 쪽에서 외래키를 가져야 한다고 생각해도 된다. 이게 헷갈린다면 DB 의 레코드 구조를 생각해보면 직관적으로 알 수 있다. Member 에서 여러 건의 Order 를 가질 수 있다는것을 표현하려면 Member#1, Order#1 인 행과 Member#1, Order#2 의 행이 있어야 한다. Order 가 여러 건인걸 표현하기 위해 User#1 의 정보가 중복되므로 Order에서 Member의 Id 외래키를 관리하는것이 합당하다.
  • Order - OrderItem: 주문은 주문 아이템을 여러건 가질 수 있다. 단 주문 아이템은 주문 자체가 생성되어야 생성될 수 있는 개념이므로 다중성의 하한이 1 이라고 생각하였다. 1 : N 의 관계이다. 주문 아이템에서 주문을 참조할 수 있어서 양방향 연관관계이다. 외래키의 주인은 주문 아이템이다. Member - Order 처럼 레코드 구조를 생각해보면 왜 OrderItem 에서 Order 의 Id 를 외래키로 참조하고 있어야 하는지 알 수 있다.
  • OrderItem - Item: 주문 아이템은 아이템을 1 건만 참조하고 있다. 아이템은 재고수량이 충분하다면 다른 여러 주문에서 참조될 수 있다. 따라서 주문아이템과 아이템은 N : 1 의 관계이다. OrderItem 의 다중성 하한은 0 이라고 생각했는데, 아이템을 주문하지 않을수도 있기 때문이다.

- JPA 코드로의 변환

Member 엔티티는 아래와 같이 작성한다.

 

@Entity
public class Member {

	@Id @GeneratedValue
	@Column(name = "MEMBER_ID")
	private Long id;
	
	private String name;
	private String city;
	private String street;
	private String zipcode;
	
	@OneToMany(mappedBy = "member")
	private List<Order> orders = new ArrayList<>();

 

Member 는 주문을 여러 건 할 수 있어서 1 : N 관계이다. 따라서 외래키는 Order 에서 관리한다. 연관관계의 주인은 Order 라서 @OneToMany 어노테이션을 사용하고 mappedBy 속성의 값은 Order 의 Member 필드인 member 가 된다.

 

Member 의 반대편인 Order 는 아래와 같다. @Table 의 이름속성으로 ORDERS 를 설정한것은 Order 가 예약어라서 테이블이름으로 사용할 수 없기 때문이다.

 

@Entity
@Table(name = "ORDERS")
public class Order {

	@Id
	@GeneratedValue
	@Column(name = "ORDER_ID")
	private Long id;
	
	@ManyToOne
	@JoinColumn(name = "MEMBER_ID")
	private Member member;
	
	@OneToMany(mappedBy = "order")
	private List<OrderItem> orderItems = new ArrayList<>();
	
	@Temporal(TemporalType.TIMESTAMP)
	private Date orderDate;
	
	@Enumerated(EnumType.STRING)
	private OrderStatus status;

 

Order 는 Member 와 N : 1 관계이다. 따라서 @ManyToOne 어노테이션을 사용하며, @JoinColumn 을 이용하여 Member의 Key인 MEMBER_ID 값을 설정한다. Order 는 OrderItem 과도 관계를 갖는다. 1 : N 관계라서 @OneToMany 어노테이션을 사용하며 mappedBy 에는 OrderItem 에서 Order를 참조하는 필드명인 order 값을 설정한다.

 

OrderItem 과 Order 는 N : 1 의 관계이기 때문에 @ManyToOne 어노테이션을 설정한다. @JoinColumn 으로 Order의 Key 컬럼인 ORDER_ID 를 할당한다. 그리고 Item 과도 관계를 갖는다. N : 1 이므로 역시 @ManyToOne 이며, @JoinColumn 은 Item 의 Key 컬럼인 ITEM_ID 이다.

 

@Entity
public class OrderItem {

	@Id
	@GeneratedValue
	@Column(name = "ORDER_ITEM_ID")
	private Long id;

	@ManyToOne
	@JoinColumn(name = "ORDER_ID")
	private Order order;

	@ManyToOne
	@JoinColumn(name = "ITEM_ID")
	private Item item;
	
	private int orderPrices;
	private int count;

 

반면 OrderItem 에서 Item 단방향 참조이기 때문에 Item 은 연관관계가 없다. 

 

@Entity
public class Item {

	@Id
	@GeneratedValue
	@Column(name = "ITEM_ID")
	private Long id;
	
	private String name;
	private int price;
	private int stockQuantity;

댓글