실전 예제 1. 요구사항 분석과 기본 매핑
요구사항 분석
- 회원은 상품을 주문할 수 있다.
- 주문 시 여러 종류의 상품을 선택할 수 있다.
기능 목록
- 회원 기능
- 회원 등록
- 회원 조회
- 상품 기능
- 상품 등록
- 상품 수정
- 상품 조회
- 주문 기능
- 상품 주문
- 주문내역조회
- 주문취소
도메인 모델 분석
- 회원과 주문의 관계 : 회원은 여러 번 주문할 수 있다. (일대다)
- 주문과 상품의 관계 : 주문할 때 여러 상품을 선택할 수 있다. 반대로 같은 상품도 여러 번 주문될 수 있다.
주문상품이라는 모델을 만들어서 다대다 관계를 일대다, 다대일 관계로 풀어냄
테이블 설계
엔티티 설계와 매핑
데이터 중심 설계의 문제점
- 현재 방식은 객체 설계를 테이블 설계에 맞춘 방식
- 테이블의 외래키를 객체에 그대로 가져옴
- 객체 그래프 탐색이 불가능
- 참조가 없으므로 UML도 잘못됨(?)
실전 예제 2. 연관관계 매핑 시작
테이블 구조
- 테이블 구조는 이전과 같다.
객체 구조
- 참조를 사용하도록 변경
가장 중요한 것은 단방향 연관관계를 정하는 것!
실전 예제 3. 다양한 연관관계 매핑
배송, 카테고리 추가 - 엔티티
- 주문과 배송은 1 : 1 (@OneToOne)
- 상품과 카테고리는 N : M (@ManyToMany)
ERD
엔티티 상세
실전 예제 4. 상속관계 매핑
요구사항 추가
- 상품의 종류는 음반, 도서, 영화가 있고 이후 더 확장될 수 있다.
- 모든 데이터는 등록일과 수정일이 필수다.
도메인 모델
도메인 모델 상세
Album.java / Book.java / Movie.java
@Entity
public class Book extends Item {
private String author;
private String isbn;
public String getIsbn() {
return isbn;
}
public void setIsbn(String isbn) {
this.isbn = isbn;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
}
Item.java
@Entity
@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;
...
}
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
- @DisriminatorColumn은 default가 name = "DTYPE"
테이블 설계
실전 예제 5 - 연관관계 관리
글로벌 페치 전략 설정
- 모든 연관관계를 지연 로딩으로
- @ManyToOne, @OneToOne은 기본이 즉시 로딩이므로 지연 로딩으로 변경
영속성 전이 설정
- Order -> Delivery를 영속성 전이 ALL 설정
- Order -> OrderItem을 영속성 전이 ALL 설정
Order.java
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "DELIVERY_ID")
private Delivery delivery;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<OrderItem> orderItems = new ArrayList<>();
이렇게 세팅하면 Order를 persist하면 OrderItem과 Delivery가 함께 persist 된다.
실전 예제 6 - 값 타입 매핑
city, street,zipcode를 Address로 생성
Address.java
@Embeddable
public class Address {
//Address
private String city;
private String street;
private String zipcode;
...
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Address)) return false;
Address address = (Address) o;
return Objects.equals(getCity(), address.getCity()) && Objects.equals(getStreet(), address.getStreet()) && Objects.equals(getZipcode(), address.getZipcode());
}
@Override
public int hashCode() {
return Objects.hash(getCity(), getStreet(), getZipcode());
}
...
- equals와 hashCode는 프록시를 고려하여 getter 메소드를 사용하여 접근한다.
다른 메소드들도 대부분 이런식으로 한다.
Member.java
@Entity
public class Member {
...
@Embedded
private Address address;
...
Delivery.java
@Entity
public class Delivery extends BaseEntity {
...
@Embedded
private Address address;
private DeliveryStatus status;
...
Address.java
@Embeddable
public class Address {
//Address
private String city;
private String street;
private String zipcode;
public Address() {
}
public Address(String city, String street, String zipcode) {
this.city = city;
this.street = street;
this.zipcode = zipcode;
}
...
'Spring > JPA' 카테고리의 다른 글
다양한 연관관계 매핑 (0) | 2023.02.22 |
---|---|
연관관계 매핑 기초 (0) | 2023.02.19 |
엔티티 매핑 (0) | 2023.02.19 |
영속성 관리 - 내부 동작 방식 (0) | 2023.02.18 |
JPA 애플리케이션 개발 (0) | 2023.02.18 |