JPA/JPA 기본

18. 실전2 객체구조를 외래키에서 참조로

sdafdq 2023. 10. 24. 08:13

이게 테이블 구조고,

객체도 이거와 별반 차이가 없었다.

 

우선은 단방향부터 잘 매핑하고, 그 후 필요하면 양방향 하겠다.

 

먼저, Member

@Entity
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name="MEMBER_ID")
    private Long id;
    private String name;
    private String city;
    private String street;
    private String zipcode;
}

바뀐 것은 없다.

 

그 다음 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;
    
    private LocalDateTime orderDate;

    @Enumerated(EnumType.STRING)
    private OrderStatus status;
}

다대일 매핑에,

Member join할 때 쓰는 열은 MEMBER_ID

Member Entity의 테이블에서 가져올 때 MEMBER_ID에 맞춰서 가져온다는 뜻 인거 같음.

(mapped by는 양방향 할 때 쓰는거다.)

 

Item 먼저,

@Entity
public class Item {
    @Id
    @GeneratedValue
    @Column(name="ITEM_ID")
    private Long id;
    private String name;
    private int price;
    private int stockQuantity;
}

참조할 외래키(연관관계) 없다. 밖에서 얘를 참조하지.

 

OrderItem

@Entity
public class OrderItem {
    @Id
    @GeneratedValue
    @Column(name="ORDER_ITEM_ID")
    private Long id;

    @ManyToOne
    @JoinColumn(name = "ITEM_ID")
    private Item item;
    @ManyToOne
    @JoinColumn(name = "ORDER_ID")
    private Order order;
    private int orderPrice;
    private int count;
}

2곳이나 참조한다.

Order와 Item

테이블 또한 ITEM_ID와 ORDER_ID라는 외래키를 가지고 있다.

가져온다.

Item은 Item 엔티티의 테이블에서 ORDER_ITEM 테이블이 가지고 있는 ITEM_ID와 맞춰서 가지고 오고,

마찬가지로 Order테이블인 ORDERS 테이블에서 ORDER_ITEM(자가자신)이 가지고 있는 ORDER_ID와 맞춰서 가지고 온다.

 

 

 

이제 양방향에 대해서 좀 고민을 해 보겠다.

양방향 설계는, 정말 필요에 따라 이루어 지는 것 이다.

일단 위에서 단방향 설계는 마쳤고,

이 양방향 설계도 하나의 예시일 뿐이다.

Member가 orders를 가지고 있는 등..

order가 orderItems를 가지고 있는 등..

 

이게 예시라서 이렇게 하긴 했는데,

Member가 orders를 객체로 가지고 있는 경우는 잘 없다고 한다.

보통 member의 id를 보고 다시 order에서 select 날려서 가져오지..

 

반면 Order가 orderItems를 가지고 있는 것은 충분히 납득이 간다.

보통 주문 정보 조회할 때 우리가 주문 날짜와 주문 상태도 (배송중 등) 확인을 하긴 하지만, 뭘 주문했는지 보는 경우, 주문한 상품에 대한 정보가 필요한 경우는 무수하다.

 

일단 객체상에 양방향 연관관계를 추가해 보자. (테이블상에는 사실 양방향 연관관계는 없다.)

@Entity
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name="MEMBER_ID")
    private Long id;
    private String name;
    private String street;
    private String zipcode;
    private String city;

    @OneToMany(mappedBy = "member")
    private List<Order> orders = new ArrayList<>();
}

member에 그냥 

@OneToMany(mappedBy = "member")
private List<Order> orders = new ArrayList<>();

하나 추가했다.

mappedBy = "member" 해서,

저 Order 엔티티의 member를 참조한다는 뜻이다. 

이건 읽기전용이고, 주인이 아니기에 이 Member에서 order값을 바꿔봤자 DB에 반영되지 않는다.

 

그 다음 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<>();
    private LocalDateTime orderDate;

    @Enumerated(EnumType.STRING)
    private OrderStatus status;
}

여기도 마찬가지로, 

@OneToMany(mappedBy = "order")
private List<OrderItem> orderItems = new ArrayList<>();

이거 하나 추가했다.

마찬가지로 OrderItem에 있는 order라고 이름지어진 변수를 참조하도록 했다.

리스트에 new ArrayList<>(); 해놓는 건 관례인데,

저렇게 해야 .add() 해도 null 에러가 안나니까.

 

이제 뭐 만든 걸 이용해서 주문을 한다고 치면 대충

Order order = new Order();
em.persist(order);

OrderItem orderItem = new OrderItem();
orderItem.setOrder(order);
em.persist(orderItem);

이런 식으로.

근데 객체지향적으로 양방향으로 짜고 싶다면,

저 orderItem의 setOrder 대신

orderItem.addOrder 만들어서 (아 근데 이게 객체지향적으로 orderItem에 order를 추가하는 게 좀 이상하긴 한데.. order에서 orderItem을 추가하는 게 더 추상적으로 맞는 듯.)

그럼 다시, Order에다 orderItem을 추가하는 방향으로,

public void addOrderItem(OrderItem orderItem){
    orderItems.add(orderItem);
    orderItem.setOrder(this);
}

이렇게 Order에 만들어서,

Order order = new Order();
em.persist(order);

OrderItem orderItem = new OrderItem();
em.persist(orderItem);
order.addOrderItem(orderItem);

대충 이런 식으로..

 

어차피 update 쿼리는 영속성 컨텍스트에서 변화를 감지해 쓰기지연 SQL 저장소에 넣어진 다음 보내질 테니.

 

 

 

사실 양방향 연관관계가 꼭 필요한 것이 아니다.

그냥 조회하면 될 일이다.

근데 이번에 Order와 OrderItem은 마치 짝꿍처럼 따라다니는 것 이다.

 

개발 상 순조롭게 하기 위해 이렇게 하는 것 이다.

단방향 설계를 잘 하는 게 중요하고,

양방향 연관관계는 필요하다고 느껴지면 그 때 하는 것이 좋다.

 

 

'JPA > JPA 기본' 카테고리의 다른 글

20. 1:N 일대다  (0) 2023.10.24
19. N : 1 다대일, 다양한 연관관계  (0) 2023.10.24
17. 양방향 연관관계 주의점  (0) 2023.10.23
16. 양방향 연관관계와 연관관계의 주인 1  (0) 2023.10.22
15. 연관관계 매핑  (0) 2023.10.22