JPA 를 사용하면서 잘못된 사용 사례 또는 효율적인 사례를 알려주는 가이드라인
- OneToOne 연관 관계
- 기본키를 공유하는 경우 MapsId 어노테이션 필수 지정하자!!
- OneToOne의 경우 단방향이 더 좋다 → 불 필요한 추가 쿼리 방지
- 양방향을 해야 한다면 ByteCode Enhancement 라이브러리를 통해 Lazy 지원을 하도록 하는게 좋다
- OneToMany 연관 관계
- Cascade는 항상 부모 측에서 자식쪽으로 전이되어야 한다
- orphanRemoval(고아객체 제거)은 부모 측에 지정되어야 한다
- mappedBy 속성은 항상 부모 측에 지정되어야 한다 → 연관 관계의 주인은 자식(외래키 소유자)이어야 한다
- 연관 관계 양측 동기화는 부모 측 도우미 메서드를 통하면 유용하다
- equals, hashcode 함수는 자식 측에서 오버라이드 되어야 하며, 보통 자동 생성된 식별자를 사용한다
- 양방향 연관 관계를 사용하고 단방향을 피해야 한다 → 불필요한 쿼리 및 테이블 생성을 방지한다
- JoinColumn 이나 OrderColumn 어노테이션으로 조금의 최적화는 가능하나 그래도 느리다
- ManyToOne 단방향은 효율적이니 사용해도 된다
- ManyToMany 연관 관계
- List<Entity> 보다는 Set<Entity> 가 성능이 더 좋다 → 삭제 시 불필요한 insert 문이 발생하지 않는다
- 비 영속 상태 일관성을 위해 LinkedHashSet을 사용하는 것이 순서가 깨지지 않는다
- 배치 삭제 시 Modifiying 어노테이션에 flush, clear 속성을 통해 영속성 컨텍스트를 클리어 해주는 것이 동기화 측면에서 유용하다
- 엔티티 그래프
- 종류
- FetchGraph: 지정된 속성만 Eager로 가져오며 나머지 속성은 Lazy 처리
- LoadGraph: 지정된 속성만 Eager로 가져오며 나머지 속성은 속성의 FetchType에 따라 처리
- 엔티티 그래프가 연관된 컬렉션을 가져오는 경우 페이지네이션은 메모리 내에서 됨으로 매우 주의해야 한다 → 사용하지 마라..
- ad-hoc Entity Graph: NamedEntityGraph 지정 없이 Repository 내에서 함수 별로 Entity 그래프 생성이 가능하다
- Sub-Graph 로 연관 컬렉션의 자식 목록도 함께 불러 올 수 있다
- Where 어노테이션은 Join Fetch, Graph 로 해결이 불가능한 경우에만 사용하자
- 엔티티
- 플루언트 스타일 셋터를 통해 읽기 쉽고 자연스러운 흐름 표현이 가능하다
- findByReference 는 실제 객체가 아닌 프록시 객체를 반환하므로 타입이 다르다
- 다른 엔티티의 연관관계로써 저장할 때 불필요한 select 를 줄여 유용하다
- 하이버네이트 5 이상부터 더티 체킹은 리플렉션이 아닌 더티 트래킹 매커니즘에 의존한다
- 하이버네이트 Bytecode Enhancement 라이브러리를 통해 활성화 가능
- 스프링 domainEvents 어노테이션을 메소드에 지정하여 엔티티가 저장(save) 될 때 이벤트 발행기로 지정가능하다
- TransactionEventListener 를 통해 도메인 이벤트의 수신자로 지정가능하다 → 어떠한 시점(commit, rollback 등)에 수행할 지도 지정 가능
- 페치(Fetch)
- JPQL로 수행되는 쿼리는 영속성 컨텍스트에 엔티티가 존재하더라도 데이터 베이스에 작업을 수행한다
- 쿼리 프로젝션은 영속성 컨텍스트에 의한 세션 수준의 반복 읽기가 무시된다
- Front와 분리된 스프링 백엔드에서 OSIV 는 그냥 꺼라
- 레포짓토리에서 프로젝션을 통해 엔티티 조회 대신 필요한 정보만 노출이 가능하다
- 영속성 컨텍스트를 우회한다 → 저장조차 하지 않음
- Query 어노테이션을 이용하는 경우 명시적으로 가져올 컬럼을 지정해야 한다
- 스프링 프로젝션은 인터페이스를 이용하고, DTO 방식은 구현 클래스를 이용한다
- 인터페이스 기반 프로젝션은 중첩이 가능하나 DTO는 불가능하다
- 클래스 파라미터를 메서드에 추가하여 하나의 메서드에 여러 프로젝션 타입 적용이 가능하다
- 업데이트가 필요한 연관관계를 가진 엔티티를 한 번에 조회할 때는 {Direction} JOIN FETCH를 사용하자
- 연관 관계의 조건 검색은 필요하나 데이터는 필요하지 않다면 {Direction} JOIN도 좋다
- 페이징이 필요하다면 {Direction} JOIN 시 연관 관계의 모든 엔티티를 명시하자
- Direction: inner, left, right
- SubSelect 어노테이션은 프로젝션으로 문제 해결이 불가능할 때 사용해야 한다
- 일부 필드만 가져오고 나중에 Lazy Fetch가 필요한 연관관계가 있을 때만 사용
- 배치 처리
- 배치 크기는 hibernate.jdbc.batch_size 로 조정하는데 5~30을 권장한다
- Mysql은 rewriteBatchedStatements 옵션으로 SQL을 묶어서 전송할 수 있다
- spring jpa의 saveALL 의 단점
- 영속성 컨텍스트의 플러시, 클리어 제어 불가
- persist 대신 merge 사용으로 불필요한 select 발생 가능
- 불필요한 데이터 반환으로 메모리 사용량 증가
- JPA BatchExecutor를 통해 배치 처리 가능
- 영속성 컨텍스트 동기화에 주의해야 한다
- 자동 낙관적 잠금(version)이 무시된다
- Cascade, OrphanRemoval 이 동작하지 않는다
- 부모-자식 관계의 배치 등록 시 배치 순서를 지정하여 최적화가 가능하다 → hibernate.order_insert=true
- 하이버네이트의 배치 처리는 한 테이블에 대해서만 처리가능하므로 새로운 테이블은 새로운 배치로 처리하기 때문이다
- 배치 삭제 시에는 연관 관계를 먼저 해제한 후 delete 또는 deleteAll 을 호출하는 것이 효율이 좋다
- BatchSize 어노테이션을 통해 N+1을 조금이나마 완화할 수 있으나 JOIN FETCH 나 그래프를 사용하는 것이 더 좋다
- 컬렉션
- 단방향 일대다 연관 관계를 표현하는 좋은 방법
- elementCollection, CollectionTable 어노테이션을 통해 정의한다
- 자주 업데이트되는 컬렉션에서는 불필요한 쿼리가 발생하기 때문에 추천하지 않는다
- OrderColumn 어노테이션을 통해 최적화가 가능하지만 불필요한 쿼리를 아예 제거할 수는 없다
- 커넥션과 트랜잭션
- 실제 커넥션이 필요한 데이터베이스 작업이 생길 때 까지 커넥션을 지연하여 불필요한 커넥션 바인딩을 방지해야 좋다 → Hibernate의 autoCommit 설정을 false로 지정해야 동작한다
- 명시적으로 논리적 트랜잭션을 설정하여 불필요한 성능 저하 및 버그를 방지하자
- 데이터 수정 계획이 없다면 엔티티를 transaction(readOnly) 설정과 함께 프로젝션을 이용하는 것이 좋다 → 영속성 컨텍스트의 저장마저 건너뛰어 성능 최적화를 유도
- 오랜 기간 작업을 해야 하는 서비스 로직의 경우는 transaction을 서비스 로직에 두어 커넥션 바인딩을 지속하지 말고 repository에만 두어 짧게 커넥션을 사용하는 것이 유리 할 수도 있다
- 가장 중요한 것은 트랜잭션을 짧게 유지하는 것이다
- 식별자
- 식별자 생성 방식으로 Auto 및 Table 방식은 그냥 없다고 생각하고 쓰지마라 → 성능이 매우 나쁘다
- 식별자 생성 방식 중 시퀀스 방식은 hibernate hi/lo 알고리즘으로 최적화가 가능하다
- 다른 시스템이 insert 하여 충돌이 발생한다면 pooled{lo} 알고리즘으로 대체가 가능하다
- 업무키(neturalId)로 동등성(hashcode) 및 동일성(equals)을 오버라이드 하는 것은 좋은 선택이다
- JVM 기본 구현은 사용하지 않는 것이 좋다
- 데이터베이스 생성 식별자(identify) 를 통한 구현은 좋지 않는 방법이다
- {A}-{Sequence} 와 같은 형태의 id는 커스텀 시퀀스 id 제네레이터를 이용하는 것이 가능하다
- UUID 는 식별자 생성 방식을 Auto로 지정하고 UUID 클래스를 변수형으로 선언하면 자동으로 생성할 수 있다
- 복합 컬럼 기본키
- 조건
- EmbeddedId 방식
- IdClass 방식
- 일반적으로 mysql 은 or 보다 in 성능이 우수하다