테이블간의 연관관계 설정에 대해서 간단히 복습해본다. JPA강좌 여러개를 들었고 책도 읽어보았지만 이부분이 제일 낯설고 복잡했던것 같다.
@Entity
@Getter @Setter
@NoArgsConstructor
public class Member extends BaseEntity{
@Id @GeneratedValue
@Column(name = "member_id")
private Long id;
@Column(unique = true)
private String email;
@Column(length = 200)
private String pw;
@NotEmpty
private String name;
@Embedded
private Address address;
@JsonIgnore
@OneToMany(mappedBy = "member")
private List<Order> orders = new ArrayList<>();
@Enumerated(EnumType.STRING)
private MemberRole memberRole;
@Builder
public Member(String email, String pw, String name, Address address, List<Order> orders, MemberRole memberRole) {
this.email = email;
this.pw = pw;
this.name = name;
this.address = address;
this.orders = orders;
this.memberRole = memberRole;
}
}
@Entity
@Table(name = "orders")
@Getter @Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED )
public class Order extends BaseUserEntity{
@Id @GeneratedValue
@Column(name = "order_id")
private Long id;
@ManyToOne(fetch = LAZY)
@JoinColumn(name = "member_id")
private Member member;
@BatchSize(size = 1000)
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<OrderItem> orderItems = new ArrayList<>();
@OneToOne(fetch = LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "delivery_id")
private Delivery delivery;
private LocalDateTime orderDate; //시간
@Enumerated(EnumType.STRING)
private OrderStatus status; //상태
//연관관계 메서드
public void setMember(Member member){
this.member = member;
member.getOrders().add(this);
}
public void addOrderItem(OrderItem orderItem){
orderItems.add(orderItem);
orderItem.setOrder(this);
}
public void setDelivery(Delivery delivery){
this.delivery = delivery;
delivery.setOrder(this);
}
//생성 메서드
public static Order createOrder(Member member, Delivery delivery, OrderItem... orderItems){
Order order = new Order();
order.setMember(member);
order.setDelivery(delivery);
for(OrderItem orderItem : orderItems){
order.addOrderItem(orderItem);
}
order.setStatus(OrderStatus.ORDER);
order.setOrderDate(LocalDateTime.now());
return order;
}
//주문취소
public void cancel(){
if(delivery.getStatus() == DeliveryStatus.COMP){
throw new IllegalStateException("이미 배송이 완료된 상품은 취소가 불가능 합니다.");
}
this.setStatus(OrderStatus.CANCEL);
for(OrderItem orderItem : orderItems){
orderItem.cancel();
}
}
//조회로직
public int getTotalPrice(){
int totalPrice = 0;
for(OrderItem orderItem : orderItems){
totalPrice += orderItem.getTotalPrice();
}
return totalPrice;
}
}
여기 Member 엔티티와 Order 엔티티가있다. 나는 테이블간에 연관관계를 생각할때 실생활에서 예를들어 접근하는편이다. 이것역시 간단히 생각해보면 내가 온라인 쇼핑몰에 가서 주문을 여러건을 할 수 있다. 이러한 예시를 생각해보면 나(Member)와 주문(Order)간의 관계는 1:N관계가 성립된다. 이 두엔티티를 연관짓기 위해서@OneToMany와 @ManyToOne를 활용한다.
먼저 N쪽에 @ManyToOne을 선언하고 매핑할1의 엔티티 Member를 필드로 선언한다.
그 후
@JoinColumn(name = "member_id")
조인컬럼을 통해 Member엔티티의 어느 필드를 외래키로 쓸것인지를 명시한다. 통상적으론 N측은 1엔티티측의 pk를 외래키로 쓰기때문에 member_id를 외래키로 사용한다.
1측에서는 n측이 선언한 필드명로 매핑한다
@OneToMany(mappedBy = "member")
private List<Order> orders = new ArrayList<>();
하나의 멤버에는 여러개의 주문이있으므로 리스트로 선언한다.
하지만 여기서 member엔티티에서 order에 대한 연관정보를 가지고있을 필요가 없다.
order의 member_id만 외래키로 가지고있다면
내가 주문한 주문정보를 알고싶다면 member엔티티의 member_id로 조인하여 가져오면되고
주문에있는 멤버들을 알고싶다면 order에 member_id로 조인하면 된다.
즉 데이터베이스는 외래키 하나로 양방향 연관관계가 가능하다.
여기서
양방향 매핑의 규칙: 연관관계의 주인
양방향 연관관계 매핑 시 지켜야할 규칙이 있는데 두 연관관계 중 하나를 연관관계의 주인으로 정해야 합니다. 연관관계의 주인만이 데이터베이스 연관관계와 매핑되고 외래 키를 관리(등록, 수정, 삭제)할 수 있습니다. 반면에 주인이 아닌 쪽은 읽기만 할 수 있습니다.
어떤 연관관계를 주인으로 정할지는 mappedBy 속성을 사용하면 됩니다.
주인은 mappedBy 속성을 사용하지 않는다.
주인이 아니면 mappedBy 속성을 사용해서 속성의 값으로 연관관계의 주인을 지정해야 한다.
쉽게말하면 연관관계의 주인은 외래키를 가지고있는쪽이다 즉 order가 연관관계의 주인이 된다.
연관관계의 주인만 데이터베이스 연관관계와 매핑되고 외래 키를 관리할 수 있습니다. 주인이 아닌 반대편은 읽기만 가능하고 외래 키를 변경하지 못한다.
'JPA' 카테고리의 다른 글
즉시로딩 과 지연로딩 그리고 N+1 (0) | 2023.09.20 |
---|---|
Entity 상속 (0) | 2023.09.13 |
JPQL (0) | 2023.09.13 |
영속성 컨텍스트 (0) | 2023.09.13 |
JPA 기본문법 파악 (0) | 2023.09.11 |