JPA

[JPA] 엔티티 설계시 주의사항

코리늬 2020. 4. 30. 14:57

본 내용은 김영한님의 실전 스프링 부트 JPA 활용 강의를 수강하며 정리한 내용입니다.

 

가급적 Setter를 지양해라

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

모든 연관관계는 지연로딩(LAZY)으로 설정해라(중요)

  • 즉시로딩(Eager)의 경우 N+1 문제가 발생 할 수 있다.

  • 어떤 SQL이 실행될지 추적하기 어렵다.

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

  • 함께 조회해서 가져오고 싶은 경우 fetch join 또는 엔티티 그래프 기능을 사용해야한다.

  • xxxToOne어노테이션은 기본값이 Eager이기 때문에 LAZY로 바꿔줘야한다.

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

  • 필드에서 바로 초기화 하는것이 안전하다.

  • null에 대해 안전하다.

  • 하이버네이트에서는 엔티티를 영속화 할 때, 컬렉션을 감싸서 하이버네이트가 제공하는 내장 컬렉션으로 변경한다.

    만약 getOrders()처럼 임의의 메소드에서 컬렉션을 잘못 생성하면 하이버네이트 내부 메커니즘에 문제가 발생할 수 있다.

Member member = new Member();

System.out.println(member.getOrders().getClass());
em.persist(member);
System.out.println(member.getOrders().getClass());

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

테이블, 컬럼명 생성 전략

스프링부트에서 하이버네이트의 기존 네이밍 전략 = SpringPhysicalNamingStrategy

스프링 부트 신규 설정(엔티티(필드) -> 테이블(컬럼))

  1. 카멜 케이스 -> 언더스코어 (memberId -> member_id)
  2. . (점) -> _(언더스코어)
  3. 대문자 -> 소문자

양방향 연관관계 메소드 작성

데이터를 양쪽에 저장해야 참조해서 값을 불러올 수 있다.

public static void main(String[] args){
        Member member = new Member();
        Order order = new Order();

        member.getOrders().add(order);
        order.setMember(member);
}

이런식으로 매번 작성해야 한다면, 번거롭기도하고 실수하기 딱 좋다.

그래서 아예 연관관계 메소드를 작성하자.

package com.jpabook.jpashop.domain;

import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Entity
@Table(name = "orders")
@Getter @Setter
public class Order {

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

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "member_id")
    private Member member;

    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
    private List<OrderItem> orderItems = new ArrayList<>();

    @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "delivery_id")
    private Delivery delivery;

    private LocalDateTime orderDate; //주문시간

    @Enumerated(EnumType.STRING)
    private OrderStatus status; //주문상태 [ORDER, CANCEL]

    /* 연관관계 메소드*/
    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);
    }
}