ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 회원 도메인 개발
    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이 필요하다. 하지만 필드를 추가한다거나 그런 기능은 안되고 단순히 서로 관계 매핑만 되기 때문에 실무에서는 사용하지 않는다고 한다.

     

    아래 두 변수들은 상속관계의 연관관계이다. 카테고리에도 상속이 있을 수 있으므로 같은 엔티티에서도 관계 매핑을 해준 것이다.

Designed by Tistory.