원제: 자바 퍼시스턴스 프로그래밍 완벽 가이드
- 영속성: 개별 객체가 어플리케이션 프로세스가 보다 오래 유지 될 수 있음을 의미
- 패러다임 불일치: RDB와 객체지향 언어의 패러다임 불일치
- 합성 관계: 컨테이너 객체 없이는 대상 객체가 존재할 수 없는 유형의 연관 관계를 표현 할 수 없음
- 세분성: 사용중인 타입의 상대적 크기를 표현할 수 없음
- 상속: 다형적 연관 관계를 표현할 수 있는 명확한 방법이 없음
- 동일성: 객체의 동일성(=),동등성(equals) 는 RDB의 기본키 값 비교와 동일하지 않음
- 연관 관계: RDB는 조인, 프로젝션을 통해 연관 관계를 만들기 때문에 특정 방향으로의 탐색이 의미가 없음
- 테이터 탐색: 데이터를 접근하는 방식이 객체망(연결 그래프) 탐색이 아닌 조인으로 이루어짐
- ORM: 클래스와 데이터 베이스 스키마를 맵핑하는 메타데이터를 사용해 객체를 테이블에 투명하게 영속화하는 기술
- 영속성 단위: 데이터 베이스 커넥션, 클래스 맵핑, 기타 구성 설정을 포함한 단위
- Spring Data JPA는 JPA를 이용한 구현보다 성능 상 많은 하락이 있지만 구현 편의성이 매우 높아짐
- 도메인 모델과 메타데이터
- 투명성: 도메인 모델의 영속성 클래스와 영속성 계층 간의 관심사를 완전히 분리한다는 의미
- 잘게 세분화된 객체 모델을 이용한다는 것은 테이블보다 클래스 수가 일반적으로 더 많다
- 컬렉션 전체에 대한 setter를 제공하지 마라 → 하이버네이트 자체 구현 컬렉션을 무시하여 변경감지 및 update에 불리하다
- JPA 어노테이션은 컴파일에만 필요함으로 인스턴스 생성 시에는 라이브러리가 필요하지 않다
- Spring Data JPA
- 프로젝션
- 엔티티 인스턴스 대신 특정 속성을 기반으로 하는 인터페이스를 통해 데이터를 반환하는 기능
- 닫힌 프로젝션: 인터페이스의 모든 getter가 대상 엔티티의 프로퍼티에 해당하는 경우 쿼리 실행 최적화 가능
- 데이터를 수정하는 메서드는 @Modifying 어노테이션이 필요하며 반드시 트랜잭션 안에서 실행되어야 함
- 영속성 클래스 맵핑
- 엔티티: 영속성 식별자가 존재하여 별개의 생명 주기를 가지는 클래스
- 값 타입: 영속성 식별자가 없고 소유한 엔티티가 존재하는 클래스
- 일단 모든 것을 값 타입으로 만들고 필요한 경우 엔티티로 승격하도록 하자
- 자연키보다는 대리키를 사용하자 → 불변성을 가지며 NULL이 될 수 없고, 고유한 속성의 자연키는 생각보다 많이 없다
- 일반적으로는 insert 전에 식별자 값을 독립적으로 생성하는 전략이 최적화면에서 더 좋다
- 하이버네이트의 Dynamic Insert/Update는 SQL 쿼리를 미리 생성하지 않고 동적으로 생성한다
- 테이블 컬럼이 많아 효율이 떨어지는 경우에만 사용하자
- 값 타입 맵핑
- 필드에 transient 어노테이션 또는 키워드가 지정된 경우 영속성 대상에서 제외된다
- 모든 embedded 클래스는 소유하는 루트 엔티티 클래스의 접근 전략을 상속받는다
- hashcode와 equals 메서드를 재정의하여 모든 값을 비교하도록 하자
- 하이버네이트 Formula 어노테이션을 통해 파생 프로퍼티를 쿼리를 통해 주입받을 수 있다
- generated 어노테이션을 통해 DB 기본값으로 생성되는 값을 생성 즉시 받아 올 수 있다
- 모든 임베드된 프로퍼티가 null인 경우 객체 자체를 null로 세팅하여 불러온다
- AttributeOverride를 통해 임베드 될 객체의 컬럼을 다른 값으로 지정할 수 있다
- 컬럼 변환기는 AttributeConverter 인터페이스를 구현하고 Converter어노테이션을 지정해야 한다
- 사용하는 엔티티 클래스 필드에는 Convert 어노테이션과 컨버터를 지정해야 한다
- 하나의 필드(임베드 객체)에 여러개의 Convert 어노테이션 사용이 가능하다
- 상속 타입 맵핑
- 객체지향과 관계형의 대표적인 불일치. 객체 지향은 is-a, has-a를 지원하나 관계형은 has-a 만 지원한다
- 암시적 다형성 방식
- 일반적으로 다형성이 필요하지 않고, 상위 클래스를 수정할 일이 없는 경우 사용
- mappedSuperclass 어노테이션을 이용하여 공통 속성을 정의한다
- mappedSuperclass 클래스는 상속받은 자식 클래스의 접근 전략을 기본적으로 사용한다
- 유니온을 활용한 구체 클래스별 테이블 방식
- Inheritance(TABLE_PER_CLASS)를 이용하여 구현
- 상속 받은 수만큼 테이블을 생성하고 개별적으로 컬럼을 생성한다
- 다형적 연관관계나 쿼리가 필요하지 않은 경우 유용
- 상위 객체를 통한 쿼리를 하는 경우 UNION을 이용하여 테이합한 쿼리를 생성한다
- 클래스 계층 구조 별 테이블 방식
- Inheritance(SINGLE_TABLE)를 이용하여 구현
- 상속받은 클래스의 컬럼은 전부 Nullable해야 하며 제3 정규형을 위반한다
- 클래스의 주된 차이점이 행위인 경우 매우 유용
- 상위 클래스에서 DiscriminatorColumn 어노테이션을 통해 어떤 컬럼을 통해 하위 클래스인지 알 수 있어야 한다
- 하위 클래스는 DiscriminatorValue 어노테이션을 통해 어떤 값이 본인을 지칭하는지 알 수 있어야 한다
- 조인을 활용한 하위 클래스 별 테이블 방식
- Inheritance(JOINED)를 이용하여 구현
- 테이블이 정규화되지만 LEFT JOIN 및 CASE을 이용하여 하위 클래스를 판별한다
- 클래스의 주된 차이점이 데이터인 경우 매우 유용
- 복잡한 클래스 구조에서는 성능이 저하 될 수 있다
- 컬렉션과 엔티티 연관 관계 맵핑
- 일반적인 컬렉션 인터페이스를 사용해야 JPA의 주요 기능을 이용한 이점을 누릴 수 있다
- 배열은 지연 로딩을 할 수 없다 → 기본적으로 ElementCollection은 지연 로딩
- ElementCollection과 CollectionTable을 이용하여 엔티티가 아닌 객체를 테이블에 맵핑할 수 있다
- CollectionId 어노테이션을 이용하여 id 생성 방식을 설정 할 수 있다
- 컬렉션 변수를 새로운 변수로 직접 바인딩하는 것은 불필요한 작업을 발생시켜 성능을 떨어뜨린다
- 엔티티 연관 관계 맵핑
- 일대일
- 기본키 공유 방식: primaryKeyJoinColumn 어노테이션을 사용하면 단방향 맵핑이 가능하다
- 외래키 공유 방식: JoinColumn에 조인할 컬럼명을 명시하여 맵핑할 수 있다
- 중간 테이블 방식: JoinTable 어노테이션을 이용하여 중간 연결 테이블을 통한 맵핑이 가능하다
- 일대다
- 두 개의 bag 타입(list)를 동시에 eager(즉각) fetch 할 수는 없다 → 카테시안 곱 문제 발생
- OrderColumn을 이용하여 순서를 유지하는 fetch가 가능하다
- 다대다
- 링크(연관관계) 테이블을 이용하는 방식 → 너무 복잡하여 잘 사용되지 않는다
- 영속성
- 수명주기: 엔티티가 자신의 수명 동안 거치는 상태
- 작업단위: 상태를 변경하는 일련의 연산을 하나의 그룹으로 간주하는 것
- 엔티티의 상태
- 비영속(transient): 영속성 컨텍스트와 관련이 없는 상태
- 영속(persistent): 데이터 베이스의 대한 표현을 가진 상태
- 제거(removed): 영속된 엔티티를 작업 단위가 끝날 때 삭제하려는 상태
- 준영속(detached): 영속된 엔티티가 존재하나 영속성 컨텍스트가 닫힌 상태
- 영속성 컨텍스트
- 특정 작업 단위 내에서 발생한 데이터에 대한 모든 수정 및 상태 변경을 기억하는 서비스
- 플러시: 메모리 상태를 데이터베이스로 전파하는 과정
- 1차 캐시: 기본키를 기반으로 모든 엔티티 인스턴스를 기억
- 보장된 식별자 범위: 단일 영속성 컨텍스트 내에서 단 하나의 인스턴스만 하나의 데이터베이스 로우를 표현
- 읽기 전용 모드: 상태 스냅샷과 더티 체킹이 비 활성화되어 메모리를 아끼고 성능이 향상된다
- 동등성 구현
- merge 연산: 준영속, 비영속 엔티티 인스턴스를 영속화하는 연산
- 트랜잭션과 동시성
| --- | --- | --- | --- |
- continued..
- 페치 계획, 전략, 프로파일
- 데이터 필터링