Item 폴더안에 Category Class를 만들고
// Category Class
package jpabook.jpashop.domain.item;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
import static javax.persistence.FetchType.*;
@Entity
@Getter @Setter
public class Category {
@Id
@GeneratedValue
@Column(name = "category_id")
private Long id;
private String name;
// Category와 item은 다 : 다 관계이다.
@ManyToMany
// 중간 테이블 매핑을 해주기 때문에 JoinTable을 넣어준다.(1:다 , 다:1로 풀어내는 중간테이블)
@JoinTable(name = "category_item",
joinColumns = @JoinColumn(name = "category_id"),
inverseJoinColumns = @JoinColumn(name = "item_id"))
// Item 쪽은 mappedBy로 지정해준다.
private List<Item> items = new ArrayList<>();
@ManyToOne(fetch = LAZY)
@JoinColumn(name = "parent_id")
private Category parent;
@OneToMany(mappedBy = "parent")
private List<Category> child = new ArrayList<>();
}
// Item
package jpabook.jpashop.domain.item;
import jpabook.jpashop.exception.NotEnoughStockException;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@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 Class
@ManyToMany
@JoinTable(name = "category_item",
joinColumns = @JoinColumn(name = "category_id"),
inverseJoinColumns = @JoinColumn(name = "item_id"))
private List<Item> items = new ArrayList<>();
@ManyToOne(fetch = LAZY)
@JoinColumn(name = "parent_id")
private Category parent;
@OneToMany(mappedBy = "parent")
private List<Category> child = new ArrayList<>();
JpaApplication을 실행시켜보면 터미널에 테이블이 생성된다.
(단, 터미널에서 생성된 테이블들은 그대로 쓰면 안된다. 참고해서 DDL Script를 다듬고 검증해서 써야한다.)
h2 데이터베이스에도 컬럼들이 생성된다.
이제 엔티티 클래스 개발부분을 한번 살펴보자.
Setter를 호출하면 데이터가 변경이 되어버린다. 그래서 Setter를 엄청 많이 열어두면 엔티티가 나중에 어떻게 수정이 되는지 파악이 안된다. 여러 서비스에서 엔티티를 바꿔버리고 호출하고 있으면 혼란이 오게된다.
Setter를 막 열어두고 아무데서나 set을 쓰게된다? 그러면 나중에 어플리케이션 변경할 때 매우 불편함을 느끼게 된다.
그리고 ManyToMany를 쓰지말자. 왜냐 중간테이블에 값을 더 넣을 수가 없어서 그렇다.
- @ManyToMany 는 편리한 것 같지만, 중간 테이블( CATEGORY_ITEM )에 컬럼을 추가할 수 없고,
- 세밀하게 쿼 리를 실행하기 어렵기 때문에 실무에서 사용하기에는 한계가 있다. 중간 엔티티( CategoryItem 를 만들고
- @ManyToOne , @OneToMany 로 매핑해서 사용하자. 정리하면 대다대 매핑을 일대다, 다대일 매핑으로 풀어 내서 사용하자.
그리고 Address Class 부분에서 Protected를 넣어주어서 상속을 함부로 하지말고 new로 생성하면 안되겠네라고 생각하면 된다.
아래 부분을 참고하자.
참고!! 값 타입은 변경 불가능하게 설계해야 한다.
@Setter 를 제거하고, 생성자에서 값을 모두 초기화해서 변경 불가능한 클래스를 만들자.
JPA 스펙상 엔티티나임베디드 타입( @Embeddable )은 자바 기본 생성자(default constructor)를 public 또는 protected 로 설정해야 한다.
public 으로 두는 것 보다는 protected 로 설정하는 것이 그나마 더 안전 하다.
// Address Class
package jpabook.jpashop.domain;
import lombok.Getter;
import javax.persistence.Embeddable;
@Embeddable
@Getter
public class Address {
private String city;
private String street;
private String zipcode;
protected Address() {
}
// public으로 열려있으니까 얘를 써야겠다고 생각할 수 있다.
public Address(String city, String street, String zipcode) {
this.city = city;
this.street = street;
this.zipcode = zipcode;
}
}
<출처 김영한: 실전! 스프링 부트와 JPA 활용1 - 웹 어플리케이션 개발 >
'Spring > SpringBoot' 카테고리의 다른 글
스프링부트 개념정리 with JPA - 스프링의 핵심 & 필터 (0) | 2022.05.06 |
---|---|
도메인 분석 설계 - 엔티티 설계시 주의점 (0) | 2022.04.05 |
도메인 분석 설계 - 도메인 모델과 테이블 설계 (0) | 2022.04.02 |
SpringBoot Error 해결 (0) | 2022.04.02 |
도메인 분석 설계 - 요구사항 분석 (0) | 2022.04.01 |