-
회원 도메인 개발JPA/JPA 활용 2023. 7. 11. 18:07
위의 사진과 같이 상품을 주문하는 도메인을 개발할 것이다.
각각의 Entity들을 살펴보면 관계형 데이터베이스의 의존관계를 통해 필요한 관계형 db들끼리 연관이 되어있다.
코드로 작성을 하기 전에 어떤 변수들이 필요한지, FK(foreign key)는 어떤 db의 변수로 지정할 지 등을 먼저 정리하고 코드로 작성하는 것이 좋다.
관계형 데이터 베이스는 3가지 형태가 있다.
- ManyToOne : 여러개와 하나의 관계를 나타냄 order에서 member와의 관계
- OneToMany : 하나와 여러개의 관계를 나타냄 member에서 order의 관계
- ManyToMany : 여러개 사이의 관계를 나타냄(사용 되도록 하지 않기)
일대 다 관계에서는 FK를 다수쪽에 넣기(Member와 Order가 있으면 Order에 FK)
일대 일 관계에서는 FK를 access 많이 하는 곳에 넣기다대 다 관계는 실무에서 사용하면 안됨
Order.class
@Entity @Table(name = "orders") // 이거 하지 않으면 관례로 order로 들어감 @Getter @Setter public class Order { @Id @GeneratedValue @Column(name = "order_id") private Long id; @ManyToOne @JoinColumn(name = "member_id") // FK(foreign key) private Member member; @OneToMany(mappedBy = "order") //order에 의해서 mapping이 되었다는 뜻 private List<OrderItem> orderItems = new ArrayList<>(); @OneToOne @JoinColumn(name = "delivery_id") private Delivery delivery; private LocalDateTime orderDate; // 주문 시간, 자바에서 기본으로 지원해주는 시간기능 @Enumerated(EnumType.STRING) // Enum을 string으로 받아온다는 애노테이션 private OrderStatus status; // 주문 상태 [ORDER, CANCEL] (enum 타입) }
엔티티를 설계할 때 FK와 의존관계들을 잘 고려해야한다. order 엔티티는 member_id와 delivery_id 대해 FK를 갖는다. FK가 된다는 뜻인 @JoinColumn을 통해 관계를 맺을 value에 접근하는 것이고, FK가 아닌 값은 @JoinColumn 애노테이션을 안 붙이면 된다. OrderItem의 관계에서는 OrderItem이 FK를 갖기 때문에 @joinColum 애노테이션이 없다.
Member.class
@Entity @Getter @Setter public class Member { @Id @GeneratedValue @Column(name = "member_id") private Long id; private String name; @Embedded // Address 클래스의 @Embeddable과 이것중 하나만 적어도 되긴하는데 둘 다 적는 편 private Address address; @OneToMany(mappedBy = "member") // order table에 있는 member 필드에 의해 매핑되었다는 뜻 // 연관관계의 주인은 FK가 가까운 곳으로 하면 됨 -> order에 있는 foreign key가 주인이다. private List<Order> orders = new ArrayList<>(); }
Member 엔티티는 order 리스트와 관계가 있다. member 하나에 order 여러개가 있을 수 있으므로 @OneToMany 애노테이션을 사용했다. 또한 @Embedded로 Address를 받아왔는데 Member 엔티티에서 사용하는 내장타입의 클래스이다.
Address.class
@Embeddable // JPA의 내장타입이기 때문에 @Embeddable @Getter public class Address { private String city; private String street; private String zipcode; }
Delivery.class
@Entity @Getter @Setter public class Delivery { @Id @GeneratedValue @Column(name = "delivery_id") private Long id; @OneToOne(mappedBy = "delivery") private Order order; @Embedded private Address address; @Enumerated(EnumType.STRING) // EnumType은 무조건 String으로 받기 private DeliveryStatus status; // READY, COMP }
delivery 엔티티는 order 엔티티와 @OneToOne 관계를 가진다. 또한 위에서 사용했던 Address를 재사용했다.
@Enumerated는 DeliveryStatus라는 enum의 상태를 받아온다는 뜻이다.
DeliveryStatus.enum
public enum DeliveryStatus { READY, COMP }
OrderItem.class
@Entity @Getter @Setter 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") // order에 있는 id를 매핑 private Order order; private int orderPrice; // 주문 "당시"의 가격 private int count; // 주문 당시의 수량 }
orderitem 엔티티는 item, order과 관계형 db 관계인데, order_id와 item_id 모두에서 FK인 존재이다.
이렇게 access가 많은 엔티티가 FK를 가지는게 일반적이다.
Item.class
@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<>(); }
Item은 abstract class로 선언되어서 @Inheritance를 하게 된다.
@DiscriminatorColumn(name = 'dtype')은 item들을 분류하는 에노테이션이다. 예를 들어 Book.class를 보면
@Entity @DiscriminatorValue("B") @Getter @Setter public class Book extends Item{ private String author; private String isbn; }
@DiscriminatorValue("B")로 Book을 B로 분류한 것이다.
Category.class
@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<>(); // ~35까지 @ManyToOne @JoinColumn(name = "parent_id") private Category parent; @OneToMany(mappedBy = "parent") private List<Category> child = new ArrayList<>(); // 상속관계의 연관관계 (카테고리도 상속이 있을 수 있으니까 (음식에 우유, 빵이 있듯이)) }
카테고리에서 ManyToMany를 사용했는데 @JoinTable이라는 중간 테이블 매핑을 통해 category_item이라는 테이블을 생성해서 다대 다를 일대 다, 다대 일로 분리해주는 @JoinTable이 필요하다. 하지만 필드를 추가한다거나 그런 기능은 안되고 단순히 서로 관계 매핑만 되기 때문에 실무에서는 사용하지 않는다고 한다.
아래 두 변수들은 상속관계의 연관관계이다. 카테고리에도 상속이 있을 수 있으므로 같은 엔티티에서도 관계 매핑을 해준 것이다.
'JPA > JPA 활용' 카테고리의 다른 글
상품 리포지토리, 서비스 개발 (0) 2023.07.11 상품 엔티티 개발(비즈니스 로직) (0) 2023.07.11 회원 가입, 조회 기능 테스트 (0) 2023.07.11 회원 리포지토리, 서비스 개발 (0) 2023.07.11 엔티티 설계시 주의점 (0) 2023.07.11