- 산출 속성
- 데이터 베이스 컬럼에는 없으나 엔티티 속성으로 필요한 정보를 코드 상에서 생성하는 속성
- 방식
- 코드 레벨: Transient + PostLoad 어노테이션 조합으로 로드 시에 계산하여 필드에 저장한다
- 데이터 베이스 레벨: Formaula 어노테이션으로 수행할 쿼리 조각을 표기
- 페이지네이션
- 방식
- 오프셋
- 지정된 오프셋까지 데이터를 건너뛰고 레코드를 가져오는 방식
- 장점: 임의의 페이지를 지정하여 데이터를 가져올 수 있다. 전체 데이터량과 페이지 수를 알 수 있다
- 단점: 데이터가 많아질 수록 성능 저하가 심하고, 신규 등록 시 페이지 손실이 발생할 수 있다
- 키세트
- 지정된 값 이후부터 스캔을 시작하여 레코드는 가져오는 방식
- 장점: 데이터가 많더라도 성능 저하가 매우 적다. 페이지 손실이 발생하지 않는다
- 단점: 임의의 페이지로 이동할 수 없다. where 절 쿼리가 복잡해 진다
- 오프셋 방식의 최적화
- CountOver와 같은 윈도우 함수로 불필요한 count 쿼리를 제거 할 수 있다
- 데이터가 자주 변경되지 않는 다면 count 쿼리를 자주 호출 하지 않도록 변경한다
- CountOver가 불가능하다면 select count 서브 쿼리를 이용하자
- Query 어노테이션에서 JoinFetch 를 사용하는 경우 countQuery를 반드시 제공하여 사용해야 한다
- 하지만 메모리에서 페이지네이션을 처리하므로 쿼리 결과의 데이터가 많으면 절대 사용하면 안된다
- OneToMany, ManyToMany 연관 관계를 가진 엔티티에서 사용은 추천하지 않는다
- 쿼리 최적화
- JPA 콜백은 EntityListeners 어노테이션을 통해 다른 클래스에서 받도록 지정할 수 있다
- FetchMode 에 따른 N+1 문제 방지 방법
- select ( 기본값 ): BatchSize 어노테이션으로 해결
- subselect: 연관 자식을 한 번에 로드함으로 발생하지 않음
- join: Join Fetch 를 쿼리 메서드 작성 or 엔티티 그래프 사용
- Soft Delete 구현: Entity에 Where, SqlDelete 어노테이션을 이용한다
- NativeQuery는 동작하지 않으므로 직접 쿼리에 추가해야 한다
- JPQL은 where, having 절에서만 서브 쿼리 사용이 가능하므로 그 이외에는 nativequery 를 사용해야 한다
- 프로시저를 사용하고 싶다면 아직은 JdbcTemplate 를 사용하는 편이 더 유용하다
- in 절 캐싱을 위해서는 hibernate.query.in_clause_prameter_padding 옵션을 켜야 한다
- Lock
- SKIP LOCKED 속성을 이용하여 락된 로우를 건너뛰고 select 를 수행할 수 있다
- 낙관적 잠금은 버전 어노테이션 없이도 사용이 가능하며, OptimisticLockingType을 dirty로 설정하여 사용 가능하다
- 버전을 통한 잠금은 낙관적 설정인 경우 현재 트랜잭션이 끝날 때, 비관적 설정인 경우 즉시 수행된다
- 공유/읽기 잠금: 동시에 읽기를 허용하나 쓰기를 금지한다 → 쓰기 잠금을 허용하지 않음
- 배타/쓰기 잠금: 읽기/쓰기 모두 허용하지 않는다
- 특이하게 Mysql은 insert 도 잠금 범위에 있다면 쓰기가 허용되지 않는다.
- repeatable read를 database 레벨에서 지원하기 위함
- 상속
- discriminatorColumn, discriminatorValue 어노테이션을 통해 구분자 칼럼과 그 값을 지정할 수 있다
- 단일 테이블 상속 전략 ( 기본 전략 )
- 모든 클래스는 데이터베이스에서 하나의 테이블로 표현된다
- 구분자 칼럼(dtype) 가 추가되며 이를 통해 해당 클래스에 맵핑한다
- 장점: 읽기/쓰기가 빠르고 연관 관계 조회도 효율적이다
- 단점
- 하위 클래스에 Not Null 설정이 불가능하며, 테이블 역시 컬럼이 매우 많아 복잡해진다
- Query 어노테이션에 구분자 칼럼을 명시적으로 표기해야 하위 클래스만 필터링하여 불러 올 수 있다
- 조인 테이블 상속 전략
- 클래스를 기반 테이블 + 자식 테이블로 분리하여 표현된다
- 자식 테이블은 primaryKeyJoinColumn 어노테이션을 통해 기반 테이블에 대한 외래키를 지정 할 수 있다
- 제네릭 사용 시 Query 어노테이션에 #{#entityName} 으로 하위 자식을 동적으로 표현가능하다
- 장점: 하위 클래스에 Not Null 설정이 가능하고, 여러 하위 클래스 계층에 적합다
- 단점
- 조인이 많이 필요하고, INSERT 문의 성능이 떨어진다
- 다형성 쿼리에는 효율적이지 않다 → 기반 클래스를 통한 전체 조회 등
- Table Per Class 상속 전략
- 모든 계층을 개별 테이블로 표현하는 전략
- 기반 테이블 + 개별 상속 테이블만큼 테이블이 생성된다
- Mysql 과 조합이 좋지 않다 → 성능이 저하된다
- 장점: 하위 클래스에 Not Null 설정이 가능하고, 개별 클래스 하나만으로 모든 데이터를 조회 가능하다
- 단점
- Identity id 생성 전략을 사용 할 수 없다
- 다형성 쿼리에는 매우 효율적이지 않다 → Union 을 통해 조회한다
- MappedSuperclass 어노테이션
- 클래스 별 테이블 전략과 유사하지만 기반 클래스가 테이블로 구체화 되지 않는다
- 장점: 기반 클래스 만의 데이터가 필요하지 않은 경우 효율적
- 단점
- 기반 클래스에 대한 쿼리가 불가능하다
- 다형성 쿼리 및 연관 관계는 허용되지 않는다
- JPA 기본 사항
- 영속성 유닛: 엔티티 매니저 팩토리 + 엔티티 매니저 + 영속성 컨텍스트를 포함하는 종합 정보 객체
- 엔티티 매니저 팩토리: 온디맨드 용 엔티티 매니저를 생성하는 팩토리
- 엔티티 매니저
- 영속성 컨텍스트(1차 캐시) + 엔티티 매니저가 포함된 정보 객체
- 하나의 물리적 트랜잭션 당 한 개의 영속성 컨텍스트가 존재해야 한다
- 엔티티 매니저를 통해 엔티티를 조작한다
- SQL 이상 현상과 격리 수준
- 격리 수준을 완화하면서 성능을 올릴 때 생기는 무결성 이상 현상
- 더티 쓰기: 업데이트 손실 → 현존 데이터베이스는 대부분 이 현상을 막는다
- 더티 읽기
- 커밋 되지 않는 데이터를 읽을 수 있을 때 발생 → 나중에 롤백 가능성이 있음
- READ-UNCOMMITTED 격리 수준에서 발생 가능
- 반복 불가능한 읽기
- 동일 트랜잭션에서 다시 데이터를 읽을 때 변경된 데이터를 읽는 현상
- READ-COMMITTED 격리 수준에서 발생 가능
- 팬텀 리드
- 읽기 왜곡
- 쓰기 왜곡
- 업데이트 손실
- 스프링 트랜잭션 종류
- 플러싱
- 팁
- FlexyPool: 커넥션 풀을 효율적으로 설정하는 유용한 라이브러리