[ ] 실무에선 @ManyToMany
는 사용하지 말자!
→ CategoryItem
같은 중간 엔티티를 만들고 @ManyToOne
, @OneToMany
로 매핑해서 사용하자 !
<aside>
💡 외래 키(FK)
가 있는 곳을 “연관관계의 주인”으로 정해라.
→ “연관관계의 주인”은 단순히 외래 키(FK)
를 누가 관리하냐의 문제이지 비즈니스상 우위에 있는 것이 아니다 !
ex) “자동차”와 “바퀴”가 있으면, 일대다 관계
에서 항상 다쪽에 외래 키가 있으므로 외래 키가 있는 “바퀴”를 연관관계의 주인으로 정한다.
→ “자동차”를 연관관계의 주인으로 정하면 자동차가 관리하지 않는 바퀴 테이블의 외래 키(FK)
값이 업데이트 되므로 관리와 유지보수가 어렵고, 추가적인 업데이트 쿼리가 발생하는 성능 문제도 있다.
</aside>
[ ] “연관관계의 주인” = 어떤 값을 바뀌었을 때 변경될 외래 키(FK)
를 갖고 있을 테이블
[ ] 실무에선 가급적 Getter
는 열어두고, Setter
는 꼭 필요한 경우에만 사용하자 !
→ 엔티티
를 변경할 때는 Setter
대신에 변경 지점이 명확하도록 변경을 위한 비즈니스 메서드를 별도로 제공해야 한다.
[ ] 값 타입
은 변경 불가능하게 설계해야 한다 !
→ @Setter
를 제거하고, 생성자에서 값을 모두 초기화해서 변경 불가능한 클래스를 만들자.
→ JPA 스펙상 엔티티나 임베디드 타입( @Embeddable
)은 자바 기본 생성자(default constructor)
를 public
또는 protected
로 설정해야 한다.
→ JPA가 이런 제약을 두는 이유는 JPA 구현 라이브러리가 객체를 생성할 때 리플랙션
이나 프록시
같은 기술을 사용할 수 있도록 지원해야 하기 때문이다.
엔티티
에는 가급적 Setter
를 사용하지말자 !지연 로딩('LAZY')
으로 설정 !
→ 즉시 로딩(’EAGER’)
은 예측이 어렵고, 어떤 SQL이 실행될지 추적하기 어렵다.
특히 JPQL
을 실행할 때 N+1
문제가 자주 발생한다.
→ @XToOne(OneToOne, ManyToOne)
은 모두 즉시 로딩(’EAGER’)
이 기본값으로 되어있다.컬렉션
은 필드에서 초기화하자 !
→ null
문제에서 안전하다.
→ 하이버네이트
는 엔티티
를 영속화(ex:em.persist(member))
할 때, 컬렉션을 감싸서 하이버네이트
가 제공하는 내장 컬렉션으로 변경하는데, 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
하이버네이트
기존 구현 : 엔티티
의 필드명을 그대로 테이블
의 컬럼명으로 사용스프링 부트
신규 설정 : 엔티티(필드)
→ 테이블(컬럼)
[1] 카멜 케이스
→ 언더스코어
(memberPoint → member_point)
[2] .
(점) → _
(언더스코어)
[3] 대문자 → 소문자
→ 스프링 부트
에서 하이버네이트 기본 매핑 전략을 변경해서 실제 테이블 필드명은 다르다.논리명
생성 : “명시적”으로 컬럼, 테이블명을 직접 적지 않으면 ImplicitNamingStrategy
사용물리명
적용 : 모든 논리명
에 적용되며, 실제 테이블에 적용 (username
→ usernm
등처럼 회사 룰로 변경 가능)cascade = CascadeType.ALL
를 사용하면, 엔티티 별로 일일이 persist
를 호출해줄 필요가 없다.
ex) persist(order)
를 하면 연관관계에 있는 delivery
까지 persist(delivery)
를 호출하지 않아도 된다.