-
JPA 활용 (1-2) - 도메인 분석 설계Web/JPA 2024. 1. 4. 15:25
1. 도메인 모델과 테이블 설계
- 회원,주문,상품 관계 : 회원은 여러 상품을 주문할 수 있음
주문할 때 여러 상품을 선택할 수 있다 (주문과 상품은 다대다관계)
(1 주문에 다 상품 <-> 1상품에 다 주문)
다대다 관계는 관계형 데이터베이스는 물론이고, 엔티티에서 거의 사용하지않음
따라서, 중간에 주문상품 테이블을 추가해서 다대일관계로 풀어냄
(1주문 다주문상품 <-> 다주문상품은 1주문)
- 상품 분류 : 상품은 도서,음반,영화로 구분 상품이라는 공통 속성을 사용하므로, 상속구조 표현
- 회원 : 이름,임베디드 타입 주소, 주문을 리스트로 가짐
- 주문: 한 번 주문시 여러 상품을 주문할 수 있으므로 주문과 주문상품은 일대다 관계
주문은 상품을 주문한 회원과 배송정보,주문날짜,주문상태 가지고 있음 -> 주문 상태는 열거형으로 사용
(1:다관계 > 부모 자식1자식2자식3, 다대다관계 > 부모1 자식1,자식2, 부모2 자식1 자식2)
- 주문상품 : 주문한 상품 정보와 주문 금액,주문 수량 정보를 가지고 있다. (보통 OrderLine, LineItem으로 많이 표현)
- 상품 : 이름, 가격, 재고수량을 가지고 있다. 상품의 종류는 도서,음반,영화가 있는데 각각 속성마다 다름
- 배송: 주문시 하나의 배송정보 생성 주문과 배송은 일대일 관계
-카테고리: 상품과 다대다 관계를 맺음 parent,child 부모 자식 카테고리를 연결한다.
-주소:값 타입(임베디드타입) 회원과 배송에서 사용
* 주문과 회원은 동급의 객체, 회원을 통해서 항상 주문이 일어나는게 아니라 주문을 생성할 때 회원이 필요함
주문 내역이 필요하면 멤버를 찾아서 주문을 보는게 아니라, 주문 자체에 필터링으로 멤버를 넣기
> 회원이 주문을 참조하지않고, 주문이 회원을 참조하는 것으로 충분하다 (일대다관계)
* 테이블 명이 ORDER가 아니라 ORDERS인 것은 데이터베이스가 order by가 예약어이기 때문이다.
2. 연관관계 매핑분석
> 회원과 주문: 일대다, 다대일의 양방향 관계이다. -> 연관관계의 주인을 정해야함 -> 외래 키가 있는 주문을 연관관계의 주인으로 정하는 것이 좋다. 따라서 Order.member를 ORDERS.MEMBER_ID 오래키와 매핑한다.
단순히 읽기만 하는 연관관계 주인 쪽에 값을 세팅해야 거울 쪽은 단순하게 조회용으로 사용
>주문상품과 주문: 다대일 양방향 관계 외래키가 주문상품에 있음, 주문 상품이 연관관계 주인 그러므로 OrderItem.item을 ORDER_ITEM.ITEM_ID외래키와 맵핑한다.
>주문상품과 상품: 다대일 단방향관계 OrderItem.item을 ORDER_ITEM.ITEM_ID 외래키와 맵핑
(단방향관계는 한쪽에서만 참조 가능한 관계 -> 주문상품은 상품이 있지만, 상품은 주문상품이 없음)
>주문과 배송: 일대일 양방향관계 Order.delivery를 ORDERS.DELIVERY_ID 외래키와 매핑함
> 카테고리와 상품 : @ManyToMany사용해서 맵핑 (실무에서는 사용하면 안됨)
* 외래키가 있는 곳을 연관관계의 주인으로 설정
> 연관관계의 주인은 단순히 외래키를 누가 관리하느냐의 문제임 비지니스상의 우위가 아님
> 연관관계 주인이 업데이트 되면 나머지도 업데이트 ?
3. 엔티티 클래스 개발
- 실무에서는 Getter, Setter는 꼭 필요한 경우에만 사용하는 것을 추천
* 꼭 필요한 별도의 메서드를 제공하는 것이 가장 이상적임 - 하지만 Getter경우 모두 열어두는 것이 편리하다.
Setter는 엔티티 변경시 추적하기 힘들어진다.
@Entity @Getter @Setter public class Member{ @Id @GeneratedValue @Column(name="member_id") private Long id; private String name; @Embedded private Address address; @OneToMany(mappedBy = "member") private List<Order> orders= new ArrayList<>(); }
- 엔티티 식별자는 id pk컬럼은 member_id사용
> 주문
@Entity @Table(name = "orders") @Getter @Setter public class Order { @Id @GeneratedValue @Column(name = "order_id") private Long id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "member_id") private Member member; //주문 회원 @OneToMany(mappedBy = "order", cascade = CascadeType.ALL) private List<OrderItem> orderItems = new ArrayList<>(); @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @JoinColumn(name = "delivery_id") private Delivery delivery; //배송정보 private LocalDateTime orderDate; //주문시간 @Enumerated(EnumType.STRING) private OrderStatus status; //주문상태 [ORDER, CANCEL] //==연관관계 메서드==// 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); } } //주문상태 package jpabook.jpashop.domain; public enum OrderStatus { ORDER, CANCEL }
- cascade 옵션은 persist의 전파 > 원래 테이블 persist하고, 나머지도 (임베디드 타입 등 엔티티) persist해야 영속성으로 넘어가는데, 해당 옵션이 있으면 전파되서 같이 넘어감
- Delivery 배송정보: 1대1관계라 외래키 어디에 둘지 선택해야한다. 보통 엑세스가 많은 쪽에 둔다.
- 연관관계 메서드 : 양방향 연관관계에서 주로 설정 하나의 엔티티가 여러 엔티티에 연관관계에 따라 해당 메서드를 엔티티 내부에 둠
> 상품주문
@Entity @Table(name = "order_item") @Getter @Setter public class OrderItem { @Id @GeneratedValue @Column(name = "order_item_id") private Long id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "item_id") private Item item; //주문 상품 @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "order_id") private Order order; //주문 private int orderPrice; //주문 가격 private int count; //주문 수량 }
> 상품엔티티
@Entity @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "dtype") @Getter @Setter public abstract class Item { @Id @GeneratedValue @Column(name = "item_id") private Long id; private String name; private int price; private int stockQuantity; @ManyToMany(mappedBy = "items") private List<Category> categories = new ArrayList<Category>(); }
- @Ingeritance : 상속관계를 잡아주기 위해 사용, 싱글테이블 전략을 사용 < 자식들은 특정 컬럼으로 구분해서 한 테이블에 때려박음
>배송 엔티티
@Entity @Getter @Setter public class Delivery { @Id @GeneratedValue @Column(name = "delivery_id") private Long id; @OneToOne(mappedBy = "delivery", fetch = FetchType.LAZY) private Order order; @Embedded private Address address; @Enumerated(EnumType.STRING) private DeliveryStatus status; //ENUM [READY(준비), COMP(배송)] }
- enum타입을 사용할때는 STRING으로 두자
> 배송상태
package jpabook.jpashop.domain; public enum DeliveryStatus { READY, COMP }
>카테고리
@Entity @Getter @Setter public class Category { @Id @GeneratedValue @Column(name = "category_id") private Long id; private String name; @ManyToMany @JoinTable(name = "category_item", joinColumns = @JoinColumn(name = "category_id"), inverseJoinColumns = @JoinColumn(name = "item_id")) private List<Item> items = new ArrayList<>(); @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "parent_id") private Category parent; @OneToMany(mappedBy = "parent") private List<Category> child = new ArrayList<>(); //==연관관계 메서드==// public void addChildCategory(Category child) { this.child.add(child); child.setParent(this); } }
- 자기자신과 연관관계를 맺기 위해 여러 설정을함 아직은 잘 모르겠다!(01-04)
*ManyToMany는 사용 금지 -> 편리할 것 같지만 중간 테이블에 칼럼추가 불가, 세밀한 쿼리 실행이 어려움
실무에서는 중간 엔티티를 만들고 @ManyToOne, OneToMany를 사용함
> 주소타입
@Embeddable @Getter public class Address { private String city; private String street; private String zipcode; protected Address() { } public Address(String city, String street, String zipcode) { this.city = city; this.street = street; this.zipcode = zipcode; } }
- 값타입: address와 같은 임베디드 타입 JPA로 생성된 것 그대로 테이블로 사용하지는 말자 검증 필요
값 타입은 변경 불가능하게 설계해야함 -> @Setter를 제거하고 생성장서 값을 모두 초기화해서 변경 불가능한 클래스로 만들자
JPA 스펙상 엔티티나 엠베디드 타입은 자바 기본 생성자를 public 또는 protected로 설정 (protected면 외부에서 기본생성자는 호출못함)
JPA 구현 라이브러리가 객체를 생성할 때 리플랙션 같은 기술을 사용할 수 있도록 지원하기 위해.. 이런 제한이 있다함
'Web > JPA' 카테고리의 다른 글
JPA (2) - API 개발 시 기본으로 지켜야 할 사항 (0) 2024.01.22 JPA (11) - 다형성 쿼리,엔티티 직접 사용,정적쿼리,벌크연산 (0) 2024.01.12 JPA - 프로젝트 생성 (0) 2024.01.11 JPA 활용 (1-3) - 엔티티 설계시 주의점 (0) 2024.01.04 JPA 활용 (1) - 프로젝트 환경설정 (1) 2024.01.04