스프링부트 JPA 웹 앱 구축 - 도메인 엔티티 클래스와 연관 관계 매핑

agugu95·2020년 8월 2일
0

목표

실전! 스프링 부트와 JPA를 활용한 웹 애플리케이션 구축 1을 공부하며 기록
도메인 주도 설계를 통한 스프링 starter 웹 앱을 구성

도메인


  • 연관 관계 매핑을 이해할 것
  • 앞서 말했듯이 실제 N:N 매핑은 안됨

엔티티 클래스

  • 도메인 엔티티 구성 시 이런 모습을 띄는데 엔티티의 구성에서 중요한 건
    DB와 패러다임을 일치 시키는 것
  • 제약 사항
    • DB는 N:N 매핑을 할 수 없다
      N:N 매핑은 1:N:1로 변환 해야한다
    • JPA가 Id 찾기 위해선 어노테이션을 통해 PK 키 매핑을 해야한다.
    • @GenareteValue
      PK 매핑을 DB에 위임, 키 시퀀스를 알아서 해줌
      Int의 경우 AUTO_INCREMANT
    • 연관 관계 매핑에서는 서로 동일한 필드를 참조하므로 연관 관계의 주인이 필요하다
      1:N 매핑의 경우 보통 FK와 가까운 쪽이 주인이 되고
      1:1 매핑의 경우 데이터 접근이 많은 쪽이 주인이 된다

이 외에는 JPA 연관 관계 매핑을 참조

1:N 매핑의 경우

@Entity
@Getter @Setter
public class Member {
    @Id @GeneratedValue
    @Column(name = "member_id")
    private Long id;
    private String username;

    @Embedded // 내장 타입 임베딩
    private Address address;

    @OneToMany (mappedBy = "member") // 읽기 전용
    private List<Order> orders = new ArrayList<>();
}
  • 데이터 접근을 위해서 Getter는 열지만 Setter의 경우 원래 최대한 자제해야 함.
  • @Entity 엔티티가 될 클래스
  • @GeneratedValue를 통해 키를 매핑
  • @Column의 경우 기본적으로 필드 명을 따라가지만 명시적 타이핑을 선호
  • 멤버는 Order를 여러 개 가질 수 있기에 컬렉션 할당
  • @OneToMany(mappedBy = "member")
    하나의 멤버가 여러 개를 가질 수 있고 FK 연결이 되어있는데 멤버를 통해 주문을 변경하는 것보다 주문을 통해 변경된 멤버가 멤버로 들어가는게 올바른 동작 구조임
@Entity
@Table(name = "orders")
@Getter @Setter
public class Order {

    @Id
    @GeneratedValue
    @Column(name = "order_id")
    private String id;

    @ManyToOne
    @JoinColumn (name = "member_id")
    private Member member;

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

    @OneToOne
    @JoinColumn(name = "delivery")
    private Delivery delivery;

    private LocalDateTime orderDate;

    @Enumerated(EnumType.STRING)
    private OrderStatus status; // ORDER, CANCEL
}
  • @Id @GeneratedValue
    동일하고 컬럼만 명시적으로 변경
  • @ManyToOne
    멤버는 여러 Order를 가질 수 있지만 Order는 Member 1개밖에 없음
  • @JoinColumn(name = "member_id")
    외래키 주인인 Order가 조인 할 테이블 컬럼은 member_id임

@Enumrated 사용 시 주의사항

  • Enumrated를 사용하면 Enum 매핑 가능
  • 기본적 Enum 타입은 ORDINAL인데 이건 숫자를 자동으로 증가 시킴
    하지만 중간에 값이 추가되거나 삭제될 시 숫자가 밀려버리는 등의 문제점 발생
    절대 절대 절대 절대 사용하지 말 것
  • 반드시 Enum 타입은 STRING을 사용

N:N 매핑

  • 실제 업무에서 N:N은 사용하지 않음
  • N:N 매핑은 엔티티에서 1:N:1로 구현 되기때문에 연관 테이블이 생김

  • DB는 객체지향과 패러다임 불일치로 N:N 매핑의 경우 연관 테이블을 통해 매핑
@Entity
@Getter
@Setter
public class Category {

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

    private String name;

    @ManyToMany // N:N 연관 관계 매핑
    @JoinTable(name = "category_item",
            joinColumns = @JoinColumn(name = "category_id"),
            inverseJoinColumns = @JoinColumn(name = "item_id"))
    private List<Item> items = new ArrayList<>();

    // 셀프 양방향 연관 관계
    @ManyToOne
    @JoinColumn(name = "parent_id")
    private Category parent;

    @OneToMany(mappedBy = "parent")
    private List<Category> child = new ArrayList<>();

}
  • @JoinTable 연관 테이블을 통해 매핑
    연관 테이블 내 컬럼을 통해 순방향 참조와 역방향 참조 구성
  • 자기 자신을 리스트로 가지는 방법은 셀프 양방향 연관 관계 매핑을 통해 구성
    parent라는 카테고리와 관계를 가지는 child를 통해 가짐

@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") // N:N 매핑
    private List<Category> categories = new ArrayList<>();
}

에러

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource
[org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; 
nested exception is org.hibernate.AnnotationException: 
mappedBy reference an unknown target entity property: jpabook.jpashop.domain.Category.parent in jpabook.jpashop.domain.item.Item.child
  • 코드는 같은데 어노테이션 에러가 뜨길래 하이버네이트 이슈인가 싶어서 하이버네이트 버젼 바꿔봄
  • 하이버네이트 바꿨는데 안되서 스프링 버젼 바꿔봄

알고보니 ITEM 추상 클래스에 집어넣어서 그런거였음 Category로 옮기니 해결

  • 이 에러는 클래스에 그런 참조나 매핑이 존재할 수 없다는 에러

0개의 댓글