JPA

SpringBoot JPA 쇼핑몰 엔티티 개발Ⅱ

빈코 2022. 9. 14. 11:54

SpringBootJPA, Thymeleaf를 사용하여 간단한 쇼핑몰을 구현하려 합니다. 프로젝트 생성이나 Gradle 빌드는 깃허브 BincoShop을 참고해주세요! 포스팅은 도메인&테이블 설계 -> 엔티티 개발 -> 회원 서비스 -> 상품 서비스 -> 주문 서비스 순으로 진행됩니다. 포스팅의 잘못된 부분은 언제든 댓글로 남겨주시면 수정하겠습니다😀

 

로고
엔티티 개발II

개요

이전 포스팅에 이어서 Category Entity를 개발합니다. 또한, 내장 타입으로 개발했던 Address를 변경 불가능하게 수정할 예정입니다. 모든 개발 과정은 김영한님의 강의를 통해 포스팅하였습니다😀

 

테이블
테이블 분석

 

Category Entity🔆

@Entity
@Getter
@Setter
public class Category {

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

    private String name;

    @ManyToMany
    @JoinTable(name = "category_item",
        joinColumns = @JoinColumn(name = "category_id"),
            inverseJoinColumns = @JoinColumn(name = "item_id")
    )
    private List<Item> items = new ArrayList<>();

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "parent_id")
    private Category parent;

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

Category Entity는 Item Entity와 다대다(@ManyToMany) 관계를 맺고 있습니다. 객체끼리의 매핑이 아니기 때문에, 가운데에 테이블(CATEGORY_ITEM)을 하나 두어서 일대다, 다대일로 풀어내야합니다. 

 

📌사실 실무에서는 @ManyToMany를 사용하지 않습니다. @ManyToMany는 편리한 것 같지만 중간 테이블에 컬럼을 추가할 수 없고, 세밀하게 쿼리를 실행하기 어렵기 때문에 실무에서 사용하기에는 한계가 있습니다.

 

Item Entity와는 다대다 매핑이므로 @JoinTable을 사용하여 관계를 맺습니다. 연관관계의 주인을 카테고리로 설정하였기 때문에, @JoinColumn을 category_id로, 반대편 @JoinColumn을 item_id로 설정하였습니다.

 

@ManyToMany(mappedBy = "items")
private List<Category> categories = new ArrayList<>();

반대로 Item Entity에서는 mappedBy를 통해 Category Entity에 의존하고 있음을 명시해줍니다.


그리고 Category Entity는 같은 Entity 내부에서 양방향 관계를 맺을 수 있습니다. 소위 한 카테고리가 다른 카테고리에 속할 수 있는 경우를 일컫습니다. 

 

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_id")
private Category parent;

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

 

parent(부모 카테고리)와 child(자식 카테고리)와의 연관관계에서 parent가 주인이 되고, 여러 자식이 있을 수 있기 때문에 List로 풀어냈습니다. 

 

Address 내장 타입🏡

@Embeddable
@Getter
public class Address {

    private String city;
    private String street;
    private String zipcode;

}

 

지난 포스팅에서는 Addres 내장 타입은 컬럼만 선언하였습니다. 지금 위 코드는 @Setter 어노테이션도 없기 때문에, 값을 넣을 방법이 없어서 쓸 수 없는 상태입니다. 하지만 그렇다고 @Setter를 넣는 방법도 좋은 방법이 아닙니다. 그럼 어떻게 해야 할까요?

 

protected Address() {

}

public Address(String city, String street, String zipcode) {
    this.city = city;
    this.street = street;
    this.zipcode = zipcode;
}

 

가장 좋은 방법은 값 타입은 변경 불가능하게 설계해야 합니다. @Setter를 사용하지 않고, 생성자에서 값을 모두 초기화해서 변경 불가능한 클래스를 만드는 것입니다.

 

JPA 스펙상 엔티티나 임베디드 타입(@Embeddable)은 자바 기본 생성자를 public 또는 protected로 설정해야 합니다. public 보다는 protected로 설정하는 것이 그나마 더 안전합니다.

 

JPA가 이런 제약을 두는 이유는 JPA 구현 라이브러리가 객체를 생성할 때 리플랙션 같은 기술을 사용할 수 있도록 지원해야 하기 때문입니다.

 

엔티티 설계시 주의점❗

엔티티에는 가급적 Setter를 사용하면 안 된다

- Setter가 모두 열려있으면 변경 포인트가 너무 많아서 유지보수가 어렵다.

 

모든 연관관계는 지연 로딩으로 설정하자

- 즉시 로딩(Eager)은 예측이 어렵고, 어떤 SQL이 실행될지 추측하기 어렵다. 특히 JPQL을 실행할 때 N+1 문제가 자주 발생한다.

- 실무에서 모든 연관관계는 지연 로딩(LAZY)으로 설정해야 한다.

- 연관된 엔티티를 함께 DB에서 조회해야 하면, fetch join 또는 엔티티 그래프 기능을 사용한다.

- @XToOne(OneToOne, ManyToOne) 관계는 기본이 즉시 로딩이므로 직접 지연 로딩으로 설정해야 한다.

 

컬렉션은 필드에서 초기화 하자

- 컬렉션은 필드에서 바로 초기화하는 것이 안전한다.

- null 문제에서 안전한다.

- 하이버네이트는 엔티티를 영속화할 때, 컬렉션을 감싸서 하이버네이트가 제공하는 내장 컬렉션으로 변경한다. 만약 getOrders()처럼 임의의 메서드에서 컬렉션을 잘못 생성하면 하이버네이트 내부 메커니즘에 문제가 발생할 수 있다. 

 

Member member = new Member();
System.out.println(member.getOrders().getClass());
em.persist(team);
System.out.println(member.getOrders().getClass());

//출력 결과
class java.util.ArrayList
class org.hibernate.collection.internal.PersistentBag

 

 


👨‍👩‍👦‍👦 오픈채팅방 운영

취업을 준비하는 예비 개발자분들을 위한 질문&답변할 수 있는 공간을 만들었습니다. 취업과 이직을 하기 위해서 어떤 걸 중점적으로 준비해야 하는지부터 포트폴리오&이력서 작성법 등 다양한 질문들을 받고 답변을 드립니다. 참여하셔서 다양한 정보 얻고 가시면 좋을 것 같네요😁

 

참여코드 : 456456

https://open.kakao.com/o/gVHZP8dg

 

비전공 개발자 취업 준비방(질문&답변)

#비전공 #개발자 #취업 #멘토링 #부트캠프 #국비지원 #백엔드 #프론트엔드 #중소기업 #중견기업 #자바 #Java #sql

open.kakao.com

 


👨‍💻 전자책 출간

아울러 제가  🌟비전공자에서 2년만에 보안 전문 중견기업으로 이직 한 방법들을 정리한 전자책을 출간 하게 되었습니다. 어떤 걸 공부해야 하는지, 이직을 위해서 무엇을 준비해야 하는지, 제가 받았던 기술 면접 리스트 등 다양한 목차로 구성되어 있습니다. 또한, 구매 시 1:1 채팅을 이용하여 포트폴리오 첨삭을 도와드리고 있습니다. 🐕전자책으로 얻은 모든 수익은 유기견 센터 '팅*벨 입양센터'에 후원될 예정입니다. 관심 있으신 분들은 아래 링크를 참고해주세요😁

https://kmong.com/gig/480954

 

비전공개발자 2년만에 중견기업 들어간 방법 | 14000원부터 시작 가능한 총 평점 0점의 전자책, 취

0개 총 작업 개수 완료한 총 평점 0점인 Binco의 전자책, 취업·이직 전자책 서비스를 0개의 리뷰와 함께 확인해 보세요. 전자책, 취업·이직 전자책 제공 등 14000원부터 시작 가능한 서비스

kmong.com


 

마치며

엔티티 설계는 이 포스팅으로 마무리됩니다. 다음 포스팅부터는 실제 비즈니스 로직을 구현을 할 예정이고, 회원 서비스부터 진행할 예정입니다. 혹여나 궁금하신 점이 있으시다면 댓글로 남겨주세요😄

 

관련 포스팅

SpringBoot JPA 쇼핑몰 도메인&테이블 설계

SpringBoot JPA 쇼핑몰 엔티티 개발 I

SpringBoot JPA 쇼핑몰 회원 서비스 개발

SpringBoot JPA 쇼핑몰 상품 서비스 개발

SpringBoot JPA 쇼핑몰 주문 서비스 I

SpringBoot JPA 쇼핑몰 주문 서비스 II

SpringBoot JPA 쇼핑몰 주문 검색 개발

 

Reference

스프링 부트와 JPA 활용 - 김영한

반응형