Entity Class 개발

유병익·2022년 11월 29일
0
post-thumbnail

참고

  • Getter, Setter 모두 제공하지 않고, 별도의 메서드를 제공하는 것이 이상적
  • 하지만, 엔티티의 데이터를 조회하는 경우가 매우 많음
    • Getter는 열어두는 것이 편리함
    • Getter는 호출만으로 값이 바뀌지는 않음
  • Setter를 호출하면 데이터가 변함
    • Setter를 남용하면 엔티티의 변경을 추적하기 힘들어짐
  • 엔티티를 변경할 때는 Setter 대신에 변경 지점이 명확하도록 변경을 위한 비즈니스 메서드를 별도로 제공해야 한다.

📌 실무에서는 가급적 Getter는 열어두고, Setter는 꼭 필요한 경우에만 사용하는 것을 추천

1. Entity 개발


1.1 Member Entity


  • Member.java
    package jpabook.jpashop.domain;
    
    import lombok.Getter;
    import lombok.Setter;
    
    import javax.persistence.*;
    import java.util.ArrayList;
    import java.util.List;
    
    @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<>();
    
    }



1.2 Order Entity


  • Order.java
    package jpabook.jpashop.domain;
    
    import lombok.Getter;
    import lombok.Setter;
    
    import javax.persistence.*;
    import java.time.LocalDateTime;
    import java.util.ArrayList;
    import java.util.List;
    
    @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(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
        @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) {
            this.orderItems.add(orderItem);
            orderItem.setOrder(this);
        }
    
        public void setDelivery(Delivery delivery) {
            this.delivery = delivery;
            delivery.setOrder(this);
        }
    }
    ``
    



1.3 OrderItem Entity


  • OrderItem.java
    package jpabook.jpashop.domain;
    
    import jpabook.jpashop.domain.item.Item;
    import lombok.Getter;
    import lombok.Setter;
    
    import javax.persistence.*;
    
    @Entity
    @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;
    }



1.4 Item


1.4.1 Item Entity


  • Item.java
    package jpabook.jpashop.domain.item;
    
    import jpabook.jpashop.domain.Category;
    import jpabook.jpashop.exception.NotEnoughStockException;
    import lombok.Getter;
    import lombok.Setter;
    
    import javax.persistence.*;
    import java.util.ArrayList;
    import java.util.List;
    
    @Entity
    @Getter
    @Setter
    @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
    @DiscriminatorColumn(name = "dtype")
    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<>();
    }



1.4.2 Album Entity


  • Album.java
    package jpabook.jpashop.domain.item;
    
    import lombok.Getter;
    import lombok.Setter;
    
    import javax.persistence.DiscriminatorValue;
    import javax.persistence.Entity;
    
    @Entity
    @Getter
    @Setter
    @DiscriminatorValue("A")
    public class Album extends Item{
        private String artist;
        private String etc;
    }



1.4.3 Book Entity


  • Book.java
    package jpabook.jpashop.domain.item;
    
    import lombok.Getter;
    import lombok.Setter;
    
    import javax.persistence.DiscriminatorValue;
    import javax.persistence.Entity;
    
    @Entity
    @Getter
    @Setter
    @DiscriminatorValue("B")
    public class Book extends Item{
        private String author;
        private String isbn;
    
        public static Book createBook(String name, int price, int stockQuantity, String author, String isbn){
            Book book = new Book();
            book.setName(name);
            book.setPrice(price);
            book.setStockQuantity(stockQuantity);
            book.setAuthor(author);
            book.setIsbn(isbn);
            return book;
        }
    
        public void changeBookInfo(String name, int price, int stockQuantity,String author, String isbn) {
            super.changeItemInfo(name, price, stockQuantity);
            this.author = author;
            this.isbn = isbn;
        }
    }



1.4.4 Movie Entity


  • Movie.java
    package jpabook.jpashop.domain.item;
    
    import lombok.Getter;
    import lombok.Setter;
    
    import javax.persistence.DiscriminatorValue;
    import javax.persistence.Entity;
    
    @Entity
    @Getter
    @Setter
    @DiscriminatorValue("M")
    public class Movie extends Item{
        private String director;
        private String actor;
    }



1.5 Delivery


1.5.1 Delivery Entity


  • Delivery.java
    package jpabook.jpashop.domain;
    
    import lombok.Getter;
    import lombok.Setter;
    
    import javax.persistence.*;
    
    @Entity
    @Getter
    @Setter
    public class Delivery {
    
        @Id
        @GeneratedValue
        @Column(name = "delivery_id")
        private Long id;
    
        @OneToOne(mappedBy = "delivery",fetch = FetchType.LAZY)
        private Order order;
    
        private Address address;
    
        @Enumerated(EnumType.STRING)
        private DeliveryStatus status; // READY, COMP
    
    }



1.5.2 DeliveryStatus Entity


  • DeliveryStatus.java
    package jpabook.jpashop.domain;
    
    public enum DeliveryStatus {
        READY,COMP
    }



1.6 Category Entity


  • Category.java
    package jpabook.jpashop.domain;
    
    import jpabook.jpashop.domain.item.Item;
    import lombok.Getter;
    import lombok.Setter;
    
    import javax.persistence.*;
    import java.util.ArrayList;
    import java.util.List;
    
    @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<>();
    
        /*
            #연관 관계 편의 메소드
                양방향 연관 관계에서 한번의 setting으로 양방향 모두 설정
         */
        public void addChildCategory(Category child){
            this.child.add(child);
            child.setParent(this);
        }
    }

실무에서는 @ManyToMany 사용 X



1.7 Address


  • Address.java
    package jpabook.jpashop.domain;
    
    import lombok.AllArgsConstructor;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
    
    import javax.persistence.Embeddable;
    
    @Embeddable
    @Getter
    @AllArgsConstructor
    public class Address {
        private String city;
        private String street;
        private String zipcode;
        protected Address(){}
    }

참고

  • 값 타입은 변경 불가능하게 설계해야 한다.
  • @Setter 제거 → 생성자에서 값을 초기화해서 변경 불가능한 클래스를 만들자.



1.8 Entity를 설계할 때 주의할 점


  • 가급적 Setter 사용 X
    • 변경 지점이 너무 많아 유지보수에 어려움
    • 리펙토링을 통해 Setter 제거
  • 모든 연관관계는 지연로딩으로 설정
    • 즉시 로딩( EAGER )은 예측이 어려움
    • 어떤 SQL이 실행될지 추적하기 어려움
    • 특히 JPQL을 실행할 때 N+1 문제 자주 발생
    • OneToOne, ManyToOne관계는 기본이 즉시로딩 → 직접 지연로딩으로 설정
  • Collection은 필드에서 초기화
    • Collection은 필드에서 바로 초기화 하는 것이 안전
      • null 문제에서 안전
    • 필드레벨에서 생성하는 것이 가장 안전하고, 코드도 간결
profile
Backend 개발자가 되고 싶은

0개의 댓글