빙응의 공부 블로그
[Spring]실전! 스프링 부트와 JPA 활용1 - 설계의 주의점 본문
📝요구사항 분석
필요한 기능을 분석하는 단계이다.
- 회원
- 회원은 이름, 주소를 통해 등록할 수 있다.
- 회원은 자신의 이름, 주소, 주문 내역을 조회할 수 있다.
- 회원은 여러개를 주문할 수 있다.
- 상품
- 상품은 이름, 가격, 재고수량을 가지고 있다.
- 상품을 주문하면 재고수량이 줄어든다.
- 상품의 종류로는 도서, 음반, 영화가 있으며 각각 사용하는 속성이 다르다.
- 주문
- 주문은 상품을 주문한 회원과 배송 정보, 주문 날짜, 주문 상태를 가지고 있다.
- 주문 상태는 주문, 취소로 표현할 수 있다.
- 주문은 여러 개의 주문 아이템을 가지고 있다.
- 주문 상품
- 주문 상품은 주문한 상품 정보와 주문금액, 주문 수량 정보를 가지고 있다.
- 주문 상품은 상품에서 상품 정보를 조회한다.
- 배송
- 주문 시 하나의 배송 정보를 가지고 있다.
- 카테고리
- 상품의 카테고리를 구분하기 위해 사용된다.
- 주소
- 값 타입으로 회원과 배송에 사용된다.
📝연관관계 매핑 분석
요구사항 | 관계 |
회원은 여러개를 주문할 수 있다. | 회원 : 주문 (일대다) |
주문은 여러 개의 주문 상품을 가지고 있다. | 주문 : 주문 상품(일대다) |
주문 상품은 상품에서 상품 정보를 조회한다. | 주문상품 : 상품(다대일) |
주문 시 하나의 배송 정보를 가지고 있다 | 주문 : 배송(일대일) |
상품의 카테고리를 구분하기 위해 사용된다. | 상품 : 카테고리(다대다) |
실습 주의점!
일반적으로 상품, 카테고리는 다대다 관계를 사용하지 않으며 그저 예제 공부를 위해 다대다로 할 것이다.
또한 주문상품, 상품 관계를 제외하고 모두 양방향으로 하여 연습을 할 것이다.!
참고로 외래 키가 잇는 곳을 연관관계 주인으로 지정할 것이다.
📝엔티티 개발 주의점!
🚩@Getter와 @Setter 허용 범위
실무에서는 가급적으로 @Getter를 열어두고 @Setter는 필요한 경우만 사용한다.
참고: 이론적으로 Getter, Setter 모두 제공하지 않고, 꼭 필요한 별도의 메서드를 제공하는게 가장 이상적이다. 하지만 실무에서 엔티티의 데이터는 조회할 일이 너무 많으므로, Getter의 경우 모두 열어두는 것이 편리하다. Getter는 아무리 호출해도 호출 하는 것 만으로 어떤 일이 발생하지는 않는다. 하지만 Setter는 문제가 다르다. Setter를 호출하면 데이터가 변한다. Setter를 막 열어두면 가까운 미래에 엔티티가 도대체 왜 변경되는지 추적 하기 점점 힘들어진다. 그래서 엔티티를 변경할 때는 Setter 대신에 변경 지점이 명확하도록 변경을 위한 비즈니스 메서드를 별도로 제공해야 한다.
🚩값 타입은 변경에 닫혀있게 설계한다.
@Embeddable
@Getter
public class Address {
private String city;
private String street;
private String zipcode;
public Address(String city, String street, String zipcode) {
this.city = city;
this.street = street;
this.zipcode = zipcode;
}
protected Address(){};
}
@Setter를 제거하고, 생성자에서 값을 모두 초기화해서 불변 클래스를 만들어야 한다.
또한 임베디드 타입은 자바 기본 생성자를 public 또는 protected로 설정해야한다.
🚩모든 연관관계는 지연로딩으로 설정한다.
- 즉시로딩은 예측이 어렵고, 어떤 SQL이 실행될지 추적이 어렵다. 특히 N+1 문제가 자주 발생
- 실무에서 모든 연관관계는 지연로딩으로 설정
- 연관관계와 함께 DB를 조회해야 하면, 페치 조인 활용
- @XToOne은 기본이 즉시 로딩이므로 직접 지연로딩을 설정해야한다.
🚩컬렉션은 필드에서 초기화한다.
컬렉션은 필드에서 바로 초기화 하는 것이 안전하다.
- null 문제에서 안전해진다.
- 하이버네이트는 엔티티를 영속화할 때, 컬랙션을 감싸서 하이버네이트의 내장 컬렉션으로 변경한다.
- 그렇기에 문제가 발생할 수 있어 그냥 엔티티 필드에서 초기화하자!
@OneToMany(mappedBy = "parent")
private List<Category> child = new ArrayList<>();
🚩연관삭제 CASCADE 전략
@OneToMany(mappedBy = "order",cascade = CascadeType.ALL)
private List<OrderItem> orderItems = new ArrayList<>();
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "delivery_id")
private Delivery delivery;
기본적인 값 세팅 후 DB에 저장하면 연관관계에 의해 다른 테이블에 자동 저장이 가능하다.
CascadeType.All은 이러한 활동의 저장, 삭제를 모두 한다는 것을 의미한다.
🚩연관관계 편의 메서드
연관관계 상에서 메서드 관리를 편하게 하기 위한 메서드이다.
양방향 관계에서 사용한다.
그 이유는 JPA적으로는 연관관계에서 연관관계 주인만 설정하면 활동에 문제 없지만
객체적 입장에서는 둘다 관계를 해줘야 하기 때문에 사용한다.
'Spring > 인프런_개념' 카테고리의 다른 글
[Spring]스프링 MVC 2편 - 파일 업로드 (1) | 2024.02.11 |
---|---|
[Spring]스프링 MVC 2편 - 스프링 타입 컨버터 (1) | 2024.02.09 |
[Spring]스프링 MVC 2편 - API 오류 처리 (0) | 2024.02.06 |
[Spring]스프링 MVC 2편 - 예외 처리와 오류 페이지 (1) | 2024.02.05 |
[Spring]스프링 MVC 2편 - 로그인 처리(필터, 인터셉터) (0) | 2024.02.04 |