오봉이와 함께하는 개발 블로그

JPA 활용 1 - 엔티티 설계시 주의점 본문

BE/JPA

JPA 활용 1 - 엔티티 설계시 주의점

오봉봉이 2022. 9. 2. 23:12
728x90

엔티티 설계시 주의점

엔티티에는 가급적 Setter를 사용하지 말자

Setter가 모두 열려있다. 변경 포인트가 너무 많아서, 유지보수가 어렵다. 나중에 리펙토링으로 Setter 제거하자.

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

@ManyToOne(fetch = FetchType.LAZY)

  • 즉시로딩(EAGER)은 예측이 어렵고, 어떤 SQL이 실행될지 추적하기 어렵다.
    • 즉시로딩을 하면 연관된 테이블을 모두 조회하는 것이다.
      • 예를 들어 Member를 조회하면 Order를 조회하고 Order와 연관된 모든 테이블을 모두 다 조회한다.
    • 특히 JPQL을 실행할 때 N+1 문제가 자주 발생한다.
  • 실무에서 모든 연관관계는 지연로딩(LAZY)으로 설정해야 한다.
  • 연관된 엔티티를 함께 DB에서 조회해야 하면, fetch join 또는 엔티티 그래프 기능을 사용한다.
  • @XToOne(OneToOne, ManyToOne) 관계는 기본이 즉시로딩이므로 직접 지연로딩으로 설정해야 한다.

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

private List<Order> orders = new ArrayList<>();

  • 컬렉션은 필드에서 바로 초기화 하는 것이 안전하다.
    • 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

테이블, 컬럼명 생성 전략

스프링 부트에서 하이버네이트 기본 매핑 전략을 변경해서 실제 테이블 필드명은 다르다.

하이버네이트 기존 구현: 엔티티의 필드명을 그대로 테이블의 컬럼명으로 사용(SpringPhysicalNamingStrategy)

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

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

cascade

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

JPA를 사용할 때 저장을 하고 싶으면 엔티티마다 persist를 해야 하는데 CascadeType.ALL를 적용하면 엔티티마다 persist하지 않아도 연관된 테이블 모두 persist된다.(delete도 마찬가지)

연관관계 편의 메소드

// 연관관계 편의 메소드
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);
}
member.getOrdes().add(order);
order.setMember(member);
->
public void setMember(Member member) {
    this.member = member;
    member.getOrders().add(this);
}

객체 상태의 양방향 연관관계에서는 두 엔티티에서 조회 등의 용이성을 위해 값을 양쪽 엔티티 모두에 값을 넣어줘야 한다. (물론 DB에는 한 테이블에만 저장)
하지만 잊어버리고 한쪽에만 값을 넣을 수 있기 때문에 편리하게 할 수 있도록 편의 메소드를 만들어 주는 것이 좋다.
연관관계 편의 메소드의 위치는 연관관계 주인이 들고 있는 것이 좋다.

출처 : 인프런 김영한 지식공유자님 강의 - 실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
728x90

'BE > JPA' 카테고리의 다른 글

JPA 활용 2 - 회원 등록 API  (0) 2022.09.03
JPA 활용 1 - 변경 감지와 병합(merge)  (0) 2022.09.03
JPA 활용 1 - 엔티티 클래스 개발  (0) 2022.09.02
JPA 활용 1 - 도메인 모델과 엔티티 설계  (0) 2022.09.02
JPA - 벌크 연산  (0) 2022.07.06
Comments