당니의 개발자 스토리

실전 예제 3 - 다양한 연관관계 매핑 본문

스프링/자바 ORM 표준 JPA 프로그래밍 - 기본편

실전 예제 3 - 다양한 연관관계 매핑

clainy 2024. 7. 17. 18:02

실전 예제 3 - 다양한 연관관계 매핑

이번 시간에는 실전 예제 3번, 이전시간까지 다양한 연관관계 맵핑을 배웠으니까, 그거를 가지고 배웠던 연관관계 맵핑을 한번 녹여보겠습니다.

지금 보시면 배송이라는 게 추가가 됐고요. 일대일 연관관계를 설명 드리려고 주문과 배송을 일대일 관계로 잡았고요. 그리고 상품과 Category다대다, *로 표시되어 있는 게 다 입니다.

이렇게 했을 때 이제 ERD는 orders의 Delivery_id, 그러니까 주문과 배송을 할 때, '일대일 관계일 때 Delivery_id, 즉 Foregin Key를 어디에 넣을 거야?' 라고 했을 때 저는 선호하는 방법인 주 테이블에 넣기로 결정을 했습니다. 그리고 CategoryItem다대다 관계에요. 하나의 상품이 여러 개 Category에 소속될 수 있고 그 반대도 성립한다는 거겠죠. 예시를 들어야 되니까 이전 시간에 다대다 쓰지 말라고 했는데 그래도 어쨌든 JPA 공식 스펙이니까 한번 예시는 들어보겠습니다.

그래서 이렇게 예시를 넣고 CATEGORY_ITEM 이라는 걸로 이렇게 맵핑 되는 걸 보여드리겠습니다.

자 그래서 이제 엔티티는 Delivery가 추가가 되고, OrderDelivery일대일 양방향 관계로 걸었구요. 보시면 Order에 Delivery가 있고 Delivery에도 Order가 있습니다. 그 다음에 Category는 CategoryItem이 있고, 다대다인데 중간 테이블 중간 엔티티없죠. Category에도 items로 해서 List가 있고, 반대로 Item에도 Category에서 해서 List 컬렉션이 있죠. 객체는 다대다 라는 게 되기 때문에 그런 거겠죠.

이제 위의 그림을 가지고 코드로 설명하겠습니다.

가장 먼저 Delivery를 만들고,

Category도 만들겠습니다. 다시 Delivery로 돌아가서,

이렇게 Entity 라는 걸 알려주고, @Id까지 잡습니다. 그 다음에 배송지 주소는

여기 Delivery에 배송 주소를 넣고요.

그 다음에 DeliveryStatus 라는 게 있는데 얘는 배송 상태이겠죠. 예시니까 Enum 클래스로 만들겠습니다.

값은 아직 넣지 말고요.

그러면 이제 Delivery는 만들었고, Category도 일단 값을 채워둘게요.

카테고리명까지 해두고, 재밌는 게 parent의 상위 Category 라는 개념이 있습니다. Category 같은 것도 맵핑이 다 됩니다. JPA에서 self로 맵핑하는 것도 다 돼요.

그래서 Category 클래스인 parent를 만들어주고, 자식 입장에서는 부모가 하나인 케이스니까 @ManyToOne 해줍니다.

그런 다음 @JoinColumn 해서 PARENT_ID 해주면 됩니다. 그 다음에 여기서 이제 양방향으로 해서 뭐 또 child를 만들 수도 있어요. 보여드리면,

이렇게 하면 양방향으로 잡힌 거예요. 위에 parent랑 셀프로 잡힌거에요.

여기에서 @OneToMany 하고, mappedBy가 parent로 잡힌 겁니다. 부모 입장에서는 자식이 여러명이니까요.

이렇게 하면 이제 이거는 Category가 쭉 내려가잖아요. 그거를 셀프로 하는걸 제가 보여드린 겁니다.

이제 Delivery부터 시작하겠습니다.

Delivery는 일단 Order랑 일대일 관계로 잡을 거예요.

Order는 이 그림에서 설명드렸듯이 테이블 관계로 DELIVERY_ID가 ORDERS에 있게 할 겁니다.

그래서 이렇게 잡고, @OneToOne 해줘야겠죠.

그리고 당연히 @JoinColumn 까지 해서 이렇게 잡아주면 됩니다. 그럼 이제 일대일 관계가 됩니다. 만약 양방향으로 하고 싶으면,

Delivery에 가서 '어떤 주문에 의해서 배송이 된 건지 알고 싶어!' 해서 이렇게 @OneToOnemappedBydelivery로 잡으시면 됩니다.

이렇게 하시면 됩니다. 그럼 이제 일대일은 됐고, Category로 가셔서,

Item이랑 다대다 관계를 하고 싶다고 하면, @ManyToMany로 잡으시면 됩니다. 이렇게 하면 하나의 Category에 여러 Item이 들어갈 수 있고, 한 Item도 여러 Category에 소속될 수 있다는 가정으로 설계를 한 거구요.

그리고 @JoinTable 이라고 적으시면 됩니다.

그 다음에 @JoinColumn을 잡아야 하는데 CATEGORY_ID 적어주시면 되고, inverseJoinColumn 이란 게 있어요.

중간 테이블 있다고 치고 ITEM_ID 하면 됩니다.

지금 이렇게 중간 테이블을 만든 거예요. 중간 테이블이 있다고 치고, 내가 Join 하는 애는 CATEGORY_ID 이고, 반대쪽이 조인해야 되는 건 ITEM_ID 라고 알려준 거예요. 이 정도만 해도 잘 돌아갈 거예요.

이제 양방향 맵핑을 해야 되니까 Item에서 내가 어디 Category 소속인지 바로 알고 싶으면 이렇게 하면 됩니다. 그럼 categories는 연관관계의 주인이 아니겠죠.

그럼 이렇게 @ManyToMany를 하고 mappedBy를 해서 items 라고 적으면 되겠죠.

 

자 이제 돌려보겠습니다.

이제 테이블 생성된 것만 보는 거예요. 제일 먼저 Category 되어있고 이 중간에 CATEGORY_ITEM, 저희가 의도한 대로 딱 맵핑이 됐죠. CATEGORY_ITEM 만들어지고 CATEGORY_ID, ITEM_ID 있고요.

그 다음에 ORDERS에 보시면 여기 DELIVERY_ID가 들어가 있죠. 딱 저희가 원하는대로 지금 테이블이 딱 만들어진 겁니다.

그 다음에 이제 연관관계 편의 메소드 이런 것들은 여러분이 자유롭게 넣으시면 돼요. 예를 들어서 '주문할 때 배송지 정보를 넣고 싶어' 그러면 여기에다가 연관관계 편의 메소드를 추가해주시면 되구요.

 

자 그래서 아까 말씀드렸다싶이,

이거는 예시이기 때문에 다대다를 쓴 거고, 실전에서는 다대다를 쓰면 안됩니다. 테이블의 다대다 관계는 중간 테이블을 이용해서 일대다, 다대일로 바꿔라. 그리고 실전에서 중간 테이블은 단순하지 않다. 그 다음에 다대다에서 제약 필드 추가나, 엔티티 테이블이 불일치한다 뭐 이런 제약이 있습니다.

자 그 다음에 이거는 @JoinColumn에 대해서 정리를 해놓은 건데요. 사실 @Column이랑 속성이 비슷하고요. '외래키를 맵핑할 건데, 뭐랑 맵핑할래?' 이름을 적어주는 정도의 차이가 있다고 보시면 됩니다. 그리고 하시다 보면 @JoinColumn 할 때 외래키가 참조하는 대상컬럼명이 좀 달라질 수가 있어요. 그런 경우에는 이제 referenceColumnName 이라고 넣어 주시면 됩니다. 이게 없으면 참조하는 테이블의 기본키 컬럼 명이 됩니다. 참고로 알아두시면 되구요. 좀 복잡한거 하실때는 바꿔서 해야 될 수도 있어요. 그 다음에 Foreign Key(DDL) 만드는거 있고, 나머지는 @Column과 속성이 똑같습니다.

그 다음에 @ManyToOne의 주요 속성 보시면 뭐 optional, fetchcascade는 뒤에서 설명 드릴 겁니다. 즉시 로딩이나, 지연 로딩 이런 것들인데 targetEntity는 모르셔도 돼요, 옛날에 제네릭이 없는 시절에는 이게 있어야 됐었거든요. 지금은 제네릭도 있고 타입 정보를 다 알아내는 방법이 있기 때문에 이게 없어도 그 타입 정보를 가지고 다 적절하게 가지고 옵니다.

그리고 @OneToMany, 일대다는 mappedBy가 있는데요, 다대일은 mappedBy가 없어요.


그 말은 다대다를 쓰면 얘는 꼭 연관관계의 주인이 되어야 된다는 겁니다. 옛날에 기억나신 분이 있을 텐데 꼼수로 insertble = false, updateable = false를 해주면 이제 그런 것처럼 쓸 수 있는데 어쨌든 스펙상은 없는 게 맞습니다. 다대일이 연관관계의 주인이 되어야 되는 거예요.


반면, 일대다는 mappedBy 속성이 있죠. 물론 일대다 단방향인 관계에서 이 mappedBy 속성을 사용 안 하면 얘도 연관관계 주인으로 만들 수 있긴 한데 어쨌든 mappedBy 속성은 있습니다. fetchcascade는 뒤에서 설명 드릴 거구요. 여기서 이제 targetEntity는 제네릭을 쓰기 때문에 지금은 옛날 버전에서 의미가 있었는데 지금은 의미가 없어요. 모르셔도 됩니다.

 

자 그러면 이제 실전 예제까지 한번 해봤습니다.

'스프링 > 자바 ORM 표준 JPA 프로그래밍 - 기본편' 카테고리의 다른 글

실전 예제 4 - 상속관계 매핑  (0) 2024.08.22
Mapped Superclass - 매핑 정보 상속  (0) 2024.08.22
다대다 [N:M]  (0) 2024.07.14
일대일 [1:1]  (0) 2024.07.07
일대다 [1:N]  (0) 2024.06.23