엔티티 설계시 주의점

PPakSSam·2022년 2월 4일
0
post-thumbnail

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

Setter가 모두 열려있으면 변경 포인트가 너무 많아서 유지보수가 어렵다.
왜냐하면 어디서 도메인의 정보가 변경되었는지 추적하기 힘들기 때문이다.
이번 예제에서는 편의상 Setter를 열어두되 나중에 리팩토링으로 Setter를 제거할 것이다.

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

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

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

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

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

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

@OneToMany(mappedBy = "member")
private List<Order> orders = new ArrayList<>();
  • 위의 코드처럼 컬렉션은 필드에서 바로 초기화하는 것이 안전하다. (Best Practice)

  • null 문제에서 안전하다.

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

  • 그런데 만약 setOrders()처럼 임의의 메서드에서 기껏해서 하이버네이트가 내장컬렉션으로 바꿔놓았는데 다시 다른 컬렉션으로 바꿔놓으면 하이버네이트 내부 메커니즘에 문제가 발생할 수 있다.

  • 따라서 필드레벨에서 생성하는 것이 가장 안전하고, 코드도 간결하다.

void 하이버네이트_내장컬렉션_확인() {
    Member member = new Member();
    System.out.println("getOrders = " + member.getOrders().getClass());

    em.persist(member);
    System.out.println("getOrders = " + member.getOrders().getClass());   
}

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

연관관계 메소드를 만들자

@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) {
        this.orderItems.add(orderItem);
        orderItem.setOrder(this);
    }
    
    public void setDelivery(Delivery delivery) {
        this.delivery = delivery;
        delivery.setOrder(this);
    }

}
  • 양방향 연관관계일 때 연관관계 주인을 정하면 하이버네이트 관점에서는 더 이상 할게없다.

  • 그러나 순수 자바 객체입장에서는 두 객체간 양방향이 있으므로 양방향에 관한 메소드를 만들어둬야 나중에 비즈니스 로직을 짤 때 문제가 생기지 않는다.

  • 예를 들어 DB에는 양방향에 대한 정보가 들어갔으나 실제 코드에서는 한쪽으로만 들어가서 순수 자바 객체에서 정보를 꺼낼 때 문제가 발생할 수 있다.

  • 위의 코드를 보며 연관관계 메소드에 대한 감을 익혀둘 것!

profile
성장에 대한 경험을 공유하고픈 자발적 경험주의자

0개의 댓글