스프링 부트와 JPA 활용1 - 2. 도메인 분석 설계
Updated:
김영한님의 인프런 강의 - 실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
위 강의를 정리한 내용입니다.
I. 도메인 모델과 테이블 설계
- 테이블간 다대다 관계는 지양하자.
- 회원이 주문리스트를 갖는 것은 보기엔 그럴듯하나 좋지 않다. 회원이 주문을 참조하는 것이 아니라 주문이 회원을 참조하는 것이 바람직하다.
- 다대다 관계는 관계형 데이터베이스에서는 다대일 + 일대다 관계로 매핑하여 구현해야 한다.
- 일대다 관계에서는 무조건 ‘다’쪽에 외래키가 존재한다.
- 외래 키가 있는 곳을 연관관계의 주인으로 정해라.
II. 엔티티 클래스 개발
어노테이션
@Column / @Table
@Column(name = “member_id”)
@Table(name = “orders”)
- 컬럼명 및 테이블명을 직접 바꿀 수 있다.
@Embeddable, @Embedded
- Entity안의 값들을 더 의미있는 값으로 응집시켜 하나의 객체로 표현하고, 결과적으로 가독성을 높일 수 있다.
- 해당 클래스에는
@Embeddable
을, 필드로 선언하는 클래스의 선언부에는@Embedded
를 붙여주자(하나만 붙여도 작동한다).
// Address.java
@Embeddable
public class Address {
}
// Member.java
@Embedded
private Address address;
@ManyToOne / @OneToMany / @OneToOne
- 각각 다대일, 일대다, 일대일에 해당하는 필드에 붙여준다.
- 매핑되는 쪽에 무엇에 의해 매핑되어지고 있는지 표기
@OneToMany(mappedBy = “member”)
@JoinColumn(name = “member_id”)
- 해당 필드를
name
에 해당하는 외래키에 매핑한다.
@Inheritance
- 관계형 데이터베이스에는 상속 관계가 없고, 슈퍼타입 서브타입이라는 유사관계만 존재한다.
- 객체의 상속관계와 DB의 슈퍼타입 서브타입 관계를 매핑하는 것을
상속관계 매핑
이라 한다. - InheritanceType에는 3가지 종류가 있다.
- JOINED
- SINGLE_TABLE
- TABLE_PER_CLASS
InheritanceTYPE.JOINED
- ITEM 테이블에는 공통정보가, 개별 정보들은 ALBUM, MOVIE, BOOK 테이블에 각각 저장된다.
- 하이버네이트의 조인 전략에서는
@DiscriminatorColumn
을 추가로 선언해야 DTYPE컬럼이 생성된다. - 테이블이 정규화 되어있다.
- 총 4개의 테이블이 생성된다.
InheritanceTYPE.SINGLE_TABLE
- 서비스 규모가 크지않고 간단히 구현하고 싶을 때 사용한다.
- 한 테이블에 다 저장하고 DTYPE으로 구분한다.
- INSERT, SELECT쿼리가 한 번에 이루어지고 조인이 필요없기에 성능이 좋다.
- 1개의 테이블만 생성된다.
InheritanceTYPE.TABLE_PER_CLASS
- JOIN전략에서 슈퍼 타입이 가지고 있던 컬럼들을 서브 타입에 모두 나눠준다.
- ITEM 엔티티는 실제 생성되지 않으므로 abstract클래스여야 한다.
- 총 3개의 테이블이 생성된다.
@Enumerated
- EnumType.ORDINAL : enum 순서 값을 DB에 저장한다.
- EnumType.STRING : enum 이름을 DB에 저장한다.
ORDINAL의 경우 enum 타입이 새로 추가되면 순서가 뒤바뀌어
심각한 오류를 일으킬 수 있다. 기본 셋팅이 ORDINAL이므로 꼭 enum타입을 사용할 때에는 STRING 을 사용하자.
참고 - Getter, Setter
- 실무에서는 가급적 Getter는 열고, Setter는 정말 필요할 때만 사용해야 한다.
- Getter는 단순히 조회하는 일만 하므로 상관없지만, Setter는 호출 시 데이터가 변경될 수 있기 때문에 추후 문제 발생시 원인추적이 어렵다.
- Setter는 로직에 따라 별도의 메서드를 제공해야 한다.
III. 엔티티 설계시 주의점
- 엔티티에는 가급적 Setter를 사용하지 말자
- 변경 포인트가 너무 많아서 유지보수가 어렵다.
- 모든 연관관계는 지연로딩으로 설정
- 즉시로딩으로 설정 시 N+1문제 자주 발생
@*ToOne
관계는 모두 기본 설정이 지연로딩이므로(fetch = FetchType.LAZY)
를 붙여주자
- 컬렉션은 필드에서 바로 초기화 하자
cascade = CascadeType.ALL
옵션을 통해 일괄persist
- 양방향 관계인 경우 연관관계 편의 메서드를 사용
//==연관관계 메서드==// public void setMember(Member member) { this.member = member; member.getOrders().add(this); } public void addOrderItem(OrderItem orderItem) { orderItems.add(orderItem); orderItem.setOrder(this); } public void setDelivery(Delivery delivery) { this.delivery = delivery; delivery.setOrder(this); }
- 왜 Order엔티티에만 적용했는가?
- 남발하면 엔티티를 저장할때 어떤 연관 엔티티들이 함께 저장되는지를 계속 추적해야 한다. 통상적으로
cascade
는 완전히 개인 소유하고 있는 엔티티일때 사용하는 것이 좋다. 예제에서는 Order가 OrderItem을 개인소유하기 때문에 cascade를 사용했지만, 사실 Delivery는 조금 애매하다.
- 남발하면 엔티티를 저장할때 어떤 연관 엔티티들이 함께 저장되는지를 계속 추적해야 한다. 통상적으로
- 왜 Order엔티티에만 적용했는가?
Leave a comment