엔티티 설계

루민 ·2023년 3월 27일
0

📌 회원

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)  // new 생성자 생성 못하게 -> Member member = new Member()
public class Member {

    @Id
    @GeneratedValue
    @Column(name = "member_id")
    private Long id;
    private String name;
    
    private String email;
    private String password;

    @Embedded
    private Address address;
    
    @Enumerated(value = EnumType.STRING)
    private Role role;
    

    @Builder
    private Member(String name, Address address, String email, String password, Role role) {
        this.name = name;
        this.address = address;
        this.email = email;
        this.password = password;
        this.role = role;
    }

} 

@NoArgsConstructor(access =AccessLevel.PROTECTED)

무분별한 Setter 사용은 객체의 일관성을 유지하기 힘들게 합니다. 때문에 new 생성자(new Member())를 사용할 수 없도록 접근 제어를 PROTECTED로 설정해주었습니다.
객체의 일관성을 유지할 수 있어야 프로그램의 유지 보수성을 끌어 올릴 수 있기 때문에 Setter는 최대한 지양해야 한다고 생각합니다.

Address 클래스

@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;
    }
}
  • 회원 엔티티는 지역명, 도로명, 우편번호의 공통된 속성을 가지고 있기 때문에 값 타입인 'Embedded 타입'을 사용하였습니다.
  • Embedded 타입 장점 : 높은 응집도, 재사용



📌 장바구니

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Cart {

    @Id
    @GeneratedValue
    @Column(name = "cart_id")
    private Long id;

    @OneToOne(fetch = FetchType.LAZY)  //모든 ~ToOne 관계는 LAZY로딩으로 설정(EAGER 쓰지 말기!!)
    @JoinColumn(name = "member_id")
    private Member member;

    private Cart(Member member) {
        this.member = member;
    }
    
    public static Cart createCart(Member member) {
        return new Cart(member);
    }
}

회원과 장바구니의 관계

한명의 회원은 하나의 장바구니를 가질 수 있고 하나의 장바구니는 하나의 회원에만 설정되므로 일대일 관계로 설정하였습니다.

지연 로딩

@ManyToOne, @OneToOne 의 관계는 디폴트값이 FetchType.Eager로 되어있습니다. 즉시로딩을 사용하게 되면 연관된 모든 엔티티 테이블에 대하여 조인하기 때문에 예상하지 못한 SQL이 발생합니다.
이로 인해 JPQL에서는 1+N 문제가 발생하게 되고 성능 저하 문제가 발생합니다.
따라서 지연 로딩(FetchType.Lazy)으로 설정하였습니다.




📌 상품

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Item {

    @Id
    @GeneratedValue
    @Column(name = "item_id")
    private Long id;
    private String name;
    private int price;
    private int stockQuantity;
    private String description;

    @Builder
    private Item(String name, int price, int stockQuantity, String description) {
        this.name = name;
        this.price = price;
        this.stockQuantity = stockQuantity;
        this.description = description;
    }
    
     public static Item createItem(String name, String description, int price, int stockQuantity) {
        return new Item(name, description, price, stockQuantity);
    }

    public void updateItem(String name, String description, int price, int stockQuantity) {
        this.name = name;
        this.description = description;
        this.price = price;
        this.stockQuantity = stockQuantity;
    }

}



📌 상품 사진

@Entity
@Table(name = "item_image")
@Getter
public class ItemImage {

    @Id
    @GeneratedValue
    @Column(name = "item_image_id")
    private Long id;
    private String originalName; //원본 파일명
    private String storeName; //서버에 저장될 경로명
    private String deleteYN; //이미지 파일 삭제 여부 

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "item_id")
    private Item item;
    
     @Builder
    private ItemImage(String originalName, String storeName, String deleteYN) {
        this.originalName = originalName;
        this.storeName = storeName;
        this.deleteYN = "N";
    }

    public void changeItem(Item item) {
        this.item = item;
    }

    public void deleteSet(String deleteYN) {
        this.deleteYN = deleteYN;
    }
}

하나의 상품에는 여러 이미지가 등록되기 때문에 다대일의 관계로 설정해주었습니다.




📌 장바구니 상품

@Entity
@Table(name = "cart_item")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class CartItem {

    @Id
    @GeneratedValue
    @Column(name = "cart_item_id")
    private Long id;
    private int count;  //장바구니 아이템 수량

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "cart_id")
    private Cart cart;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "item_id")
    private Item item;

    private CartItem(int count, Cart cart, Item item) {
        this.count = count;
        this.cart = cart;
        this.item = item;
    }

    public static CartItem createCartItem(int count, Cart cart, Item item) {
        return new CartItem(count, cart, item);
    }
}

다대다 관계

장바구니(Cart)와 상품(Item) : 하나의 장바구니에는 여러 상품이 들어갈 수 있고 하나의 상품은 여러 장바구니에 들어갈 수 있으므로 다대다의 관계가 됩니다. 따라서 Cart_Item 엔티티를 만들어서 일대다(Item:CartItem), 다대일(CartItem, cart)의 관계로 설정했습니다.




📌 주문

@Entity
@Table(name = "orders")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Order {

    @Id
    @GeneratedValue
    @Column(name = "order_id")
    private Long id;
    private LocalDateTime orderDate;

    @Enumerated(value = EnumType.STRING)
    private OrderStatus status;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "member_id")
    private Member member;

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

    private Order(OrderStatus status, Member member) {
        this.status = status;
        this.member = member;
        this.orderDate = LocalDateTime.now();
    }

    //==연관 관계 메서드==//
    public void addOrderItem(OrderItem orderItem) {
        orderItems.add(orderItem);
        orderItem.changeOrder(this);
    }

    public static Order createOrder(OrderStatus status, Member member, OrderItem... orderItems) {  //List<OrderItem> list??
        Order order = new Order(status, member);
        for (OrderItem orderItem : orderItems) {
            order.addOrderItem(orderItem);
        }
        return order;
    }
}

연관 관계

  • 회원 : 한명의 회원은 여러 주문을 할 수 있기 때문에 다대일의 관계로 설정하였습니다.
  • 주문상품 : 하나의 주문에 여러 상품이 들어가기 때문에 다대일의 관게로 설정하였습니다.

양방향 관계

최종 주문을 진행하려면 주문 상품이 있어야 하기 때문에 단방향이 아닌 양방향 관계로 설정해주었습니다.
이 때 연관관계의 주인은 ManyToOne에서 Many쪽인 Order를 연관관계의 주인으로 설정해주었습니다.

연관관계의 주인 : 객체와 테이블간에 연관관계를 맺는 차이에서 비롯되었는데 테이블은 외래키, 주키로 조인을 통해 양방향 참조가 가능하지만 객체의 경우 위의 코드와 같이 List를 통해 연결시켜주어야만 양방향이 가능합니다.
즉, 테이블은 외래키 하나로 두 테이블의 연관관계를 관리를 하지만 객체의 경우는 아닙니다.
위의 경우를 보았을 때 OrderItem, Order 둘 중 하나로 외래키를 관리해야 하는데 OrderItem엔티티의 order를 바꿧을 때 외래키(order_id)값을 업데이트 할지 Order엔티티의 orderItems의 값을 변경했을 때 외래키(order_id)값을 업데이트 할지 설정해야 합니다.
이것이 바로 연관관계의 주인으로 연관관계의 주인만이 외래키를 관리할 수 있도록 하고 주인이 아닌쪽은 읽기만 가능하도록 합니다.


 //==연관 관계 메서드==//
    public void addOrderItem(OrderItem orderItem) {
        orderItems.add(orderItem);
        orderItem.changeOrder(this);
    }

양방향 관계의 경우 순수 객체 상태를 고려해서 항상 양쪽에 값을 설정할 수 있도록 연관 관계 메서드를 작성해주었습니다.




📌 주문 상품

@Entity
@Table(name = "order_item")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class OrderItem {

    @Id
    @GeneratedValue
    @Column(name = "order_item_id")
    private Long id;
    private int count;  //주문 수량
    private int orderPrice;  //주문 총 가격

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "order_id")
    private Order order;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "item_id")
    private Item item;

    private OrderItem(int count, int orderPrice, Item item) {
        this.count = count;
        this.orderPrice = orderPrice;
        this.item = item;
    }

    public void changeOrder(Order order) {
        this.order = order;
    }

    public static OrderItem createOrderItem(int count, int orderPrice, Item item) {
        return new OrderItem(count, orderPrice, item);
    }
}

다대다 관계

  • 상품과 주문의 관계 : 하나의 주문에는 여러 상품이 들어 갈 수 있고 하나의 상품은 여러 주문에 들어갈 수 있으므로 다대다의 관계입니다. 따라서 OrderItem 엔티티를 만들어 일대다(Order:OrderItem), 다대일(OrderItem:Item)의 관계로 설정하였습니다.

0개의 댓글