Spring Boot와 JPA 활용1 - TIL(2)

YulHee Kim·2021년 9월 22일
0

Spring Boot와 JPA 활용

목록 보기
2/11
post-thumbnail

[ 참고 강의 ] 실전! 스프링 부트와 JPA 활용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>();
}

상품 - 도서 엔티티

@Entity
@DiscriminatorValue("B")
@Getter @Setter
public class Book extends Item {

    private String author;
    private String isbn;
}

상품 - 음반 엔티티

@Entity
@DiscriminatorValue("A")
@Getter @Setter
public class Album extends Item {

    private String artist;
    private String etc;
}

상품 - 영화 엔티티

@Entity
@DiscriminatorValue("M")
@Getter @Setter
public class Movie extends Item {

    private String director;
    private String actor;
}

배송 엔티티

@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; //READY, COMP
}

배송 상태

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);
    }
}

참고)
실무에서는 @ManyToMany를 사용하지 말자. 편리해보이지만 중간 테이블(CATEGORY_ITEM)에 컬러을 추가할 수 없고, 세밀하게 쿼리를 실행하기 어렵기 때문에 실무에서 사용하기에는 한계가 있다.중간 엔티티(CategoryItem)를 만들고 @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;
    }
}

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

✏️ 엔티티 설계시 주의점

엔티티에는 가급적 Setter를 사용하지 말자
Setter가 모두 열려있다. 변경 포인트가 너무 많아서 유지보수가 어렵다. ㄴ중에 리팩토링으로 세터를 제거해보겠다.

모든 연관관계는 지연로딩으로 설정!

  • 즉시로딩(EAGER)는 예측이 어렵고, 어떤 SQL이 실행될지 추적이 어렵다. 특히 JPQL을 실행할 때 N+1문제가 자주 발생한다.
  • 실무에서 모든 연관관계는 지연로딩(LAZY)으로 설정해야 한다.
  • 연관된 엔티티를 함께 DB에서 조회해야 하면 fetch join 또는 엔티티 그래프 기능을 사용한다.
  • OneToOne, ManyToOne 관계는 기본이 즉시로딩이므로 직접 지연로딩으로 설정해야 한다.

컬렉션은 필드에서 초기화하자

컬렉션은 필드에서 바로 초기화하는 것이 안전하다

  • null 문제에서 안전하다.
  • 하이버네이트는 엔티티를 영속화할 때, 컬렉션을 감싸서 하이버네이트가 제공하는 내장 컬렉션으로 변경한다. 만약 getOrders() 처럼 임의의 메서드에서 컬력션을 잘못 생성하면 하이버네이트 내부 메커니즘에 문 제가 발생할 수 있다. 따라서 필드레벨에서 생성하는 것이 가장 안전하고, 코드도 간결하다.

💡 애플리케이션 구현 준비

✏️ 애플리케이션 아키텍쳐

계층형 구조 사용

  • controller, web : 웹 계층
  • service : 비즈니스 로직, 트랜잭션 처리
  • repository : JPA를 직접 사용하는 계층, 엔티티 매니저 사용
  • domain : 엔티티가 모여 있는 계층, 모든 계층에서 사용

    개발 순서: 서비스, 리포지토리 계층을 개발하고, 테스트 케이스를 작성해서 검증, 마지막에 웹 계층 적용

다음은 회원 도메인 개발을 해보겠습니다~

profile
백엔드 개발자

0개의 댓글