ZeroZoa의 스프링 JPA 공부 기록
이 저장소는 Java Persistence API (JPA)를 학습하고 정리한 내용을 담고 있습니다. ORM의 기본 개념부터 엔티티 매핑, 영속성 관리, 다양한 연관관계 전략 등 JPA의 핵심적인 내용을 다룹니다.
실무에서 JPA를 효과적으로 사용하기 위한 핵심 전략입니다.
-
🚀 모든 연관관계는 지연 로딩(
LAZY)으로 설정하세요.- 즉시 로딩(
EAGER)은 예측하지 못한 SQL이 발생하는 주범이며, 특히 JPQL에서 N+1 문제를 일으킬 수 있습니다. @ManyToOne,@OneToOne은 기본값이 즉시 로딩이므로 반드시fetch = FetchType.LAZY옵션을 명시해야 합니다.- 성능 최적화가 필요한 경우 Fetch Join이나
@EntityGraph, **@BatchSize**를 사용해 필요한 데이터만 함께 조회하는 것을 권장합니다.
- 즉시 로딩(
-
🔗 외래 키(FK)가 있는 곳을 연관관계의 주인으로 정하세요.
- 연관관계의 주인은 외래 키를 직접 관리하는 엔티티가 되어야 합니다.
- 주인이 아닌 곳에 외래 키를 매핑하면, 연관관계 관리를 위해 추가적인 UPDATE 쿼리가 발생하여 성능 저하의 원인이 됩니다.
-
🛡️ 엔티티에는 가급적
@Setter를 사용하지 마세요.@Setter를 무분별하게 사용하면 엔티티의 변경 지점을 추적하기 어려워 유지보수가 힘들어집니다.- 대신, 변경 목적이 명확한 비즈니스 메서드를 별도로 만들어 관리하는 것이 좋습니다. (예:
cancelOrder(),updateStock(int stock))
-
💎 값 타입(Embeddable)은 불변 객체(Immutable Object)로 설계하세요.
- 객체 타입은 참조를 공유하기 때문에, 여러 엔티티에서 하나의 값 타입을 공유하면 예상치 못한 부작용(Side Effect)이 발생할 수 있습니다.
@Setter를 제거하고, 생성 시점에만 값을 할당하여 변경 불가능하게 만들면 안전하게 사용할 수 있습니다.
-
✨ 컬렉션은 필드에서 즉시 초기화하세요.
NullPointerException과 같은 예기치 않은 오류를 방지하기 위해, 컬렉션은 선언과 동시에 초기화하는 것이 안전합니다.List<Comment> comments = new ArrayList<>();
-
📤 API를 만들 때 엔티티를 외부로 직접 반환하지 마세요.
- 엔티티는 데이터베이스와 직접 연결된 핵심 도메인 객체입니다. 엔티티가 변경되면 API 스펙 전체가 흔들릴 수 있습니다.
- 반드시 DTO(Data Transfer Object)를 사용하여 API 응답에 필요한 데이터만 선별적으로 노출하세요.
-
💥 벌크 연산 후에는 영속성 컨텍스트를 초기화하세요.
- 벌크 연산은 영속성 컨텍스트를 무시하고 DB에 직접 쿼리를 실행합니다.
- 데이터 정합성 문제를 피하기 위해, 벌크 연산 수행 직후에는 영속성 컨텍스트를 초기화(
em.clear())하는 것을 권장합니다.
- 이름 그대로 객체와 관계형 데이터베이스를 매핑하는 기술입니다.
- ORM 프레임워크가 객체와 테이블 간의 패러다임 불일치 문제를 해결해 줍니다.
- Java 진영의 ORM 기술 표준 명세(Interface)입니다.
- 생산성:
save(),findById()등 기본적인 CRUD를 제공하며, DDL 자동 생성 기능도 지원합니다. - 유지보수: 필드 변경 시 모든 SQL을 수정할 필요 없이, 매핑 정보만 수정하면 되어 유지보수가 용이합니다.
- 패러다임 불일치 해결: 상속, 연관관계, 객체 그래프 탐색 등 객체와 RDB의 차이에서 오는 문제를 해결합니다.
- 벤더 독립성: 추상화된 데이터 접근 계층을 제공하여 데이터베이스 변경에 유연하게 대처할 수 있습니다.
- 성능 최적화: 1차 캐시, 쓰기 지연, 지연 로딩 등을 통해 성능 최적화를 시도할 수 있습니다.
save(S); // 엔티티 저장 및 병합
delete(T); // 엔티티 삭제
findById(ID); // ID로 엔티티 조회
findAll(); // 모든 엔티티 조회-
Chapter 1
-
Java Persistence API - JPA
- ORM Object Relational Mapping
- 이름 그대로 객체와 관계형 데이터베이스를 매핑
- ORM 프레임 워크가 객체의 관계를 매핑한다.
- ORM Object Relational Mapping
-
JPA의 특징
- 생산성
- JPA를 사용하면 컬렉션에 객체를 저장하듯이 JPA에게 저장할 객체를 전달하면 된다.
- 더 나아가 JPA에는 DDL(가령 CREATE TABLE)문을 자동으로 생성해주는 기능도 있다.
- 유지보수
- SQL을 이용하면 엔티티에 필드 하나만 추가되어도 관련된 코드를 모두 변경해야 했지만, JPA는 이런 과정을 생략해준다.
- 패러다임의 불일치 해결
- 상속, 객체 그래프 탐색, 비교와 같은 패러다임 불일치 문제를 해결해준다.
- 데이터 접근 추상화와 벤더 독립성
- 많은 경우 애플리케이션은 처음 선택한 데이터베이스 기술에 종속적이다.
- 즉 데이터베이스 변경이 매우 어렵다.
- 하지만 JPA는 애플리케이션 ↔︎ 데이터베이스 사이에 추상화된 데이터 접근 계층을 제공해 종속되지 않도록 방지해준다.
- 많은 경우 애플리케이션은 처음 선택한 데이터베이스 기술에 종속적이다.
- 성능
- JPA는 애플리케이션과 데이터베이스 사이에서 동작하므로 최적화 관점에서 시도해볼 수 있는 기회가 많다.
- 표준
- ORM표준이라 다른 구현 기술로 손쉽게 변경 가능하다.
- 생산성
-
JPA의 주요 매서드
save(S) //엔티티를 저장하고 병합delete(T) // 엔티티를 삭제findById(Id) // ID를 통해 엔티티를 조회findAll() //모든 엔티티를 조회
-
-
Chapter 2
- JPA 사용법 1. 객체 매핑
- JPA에서는 어노테이션을 통해서 객체 ↔︎ 클래스를 매핑한다.
- 어노테이션 별 특징
- @Entity
- 이 클래스를 테이블로 매핑한다고 JPA에게 선언, 엔티티 클래스라고 한다
- @Table
- 매핑할 테이블의 정보를 알려주는 어노테이션, default값은 클래스 이름이 테이블 이름으로 매핑된다.
- @Id
- 엔티티 클래스의 필드를 기본키에 매핑한다. 즉 PK가 된다.
- @Column
- 필드를 컬럼에 매핑한다.
- 매핑 정보가 없는 필드
- 어노테이션을 생략하면 필드명이 데이터베이스의 컬럼명 “그 자체”가 된다.
- @Entity
- JPA의 사용법 2. persistence.xml
- persistence.xml는 JPA를 위한 설정 정보를 관리하는 파일
- 별도의 설정 없이 JPA가 인식할 수 있다.
- JPA의 표준속성
- javax.persistence.jdbd.driver : JDBC드라이버
- javax.persistence.jdbd.user : 데이터베이스 접속 ID
- javax.persistence.jdbd.password : 데이터베이스 접속 PW
- javax.persistence.jdbd.url : 데이터베이스 접속 URL
- 데이버베이스 방언
- JPA는 서로 다른 데이터베이스의 언어도 지원한다
- JPA의 사용법 3.애플리케이션 개발
- 엔티티 매니저 설정
- 엔티티 매니저 팩토리 생성
- 첫 번째로 persistence.xml의 설정 정보를 이용해 엔티티 매니저 팩토리를 생성
- 엔티티 매니저 팩토리를 생성하는 비용은 아주 크다. 따라서 딱 한 번만 생성하고 공유해서 사용해야 한다.
- 첫 번째로 persistence.xml의 설정 정보를 이용해 엔티티 매니저 팩토리를 생성
- 엔티티 매니저 생성
- JPA의 기능 대부분을 수행한다.
- 엔티티 매너저를 사용해서 엔티티를 데이터베이스에 CRUD할 수 있다.
- 엔티티를 가상의 데이터베이스로 생각할 수 있다.
- 종료
- 사용이 끝난 엔티티 매니저는 반드시 종료해야 한다.
- 엔티티 매니저 팩토리 생성
- 트랜잭션 관리
- JPA를 사용하면 항상 트랜잭션 안에서 데이터를 변경해야 한다.
- 트랜잭션 없이 데이터를 변경하면 예외 발생
- 트랜잭션을 시작하려면 엔티티 매니저에서 트랜잭션API를 받아와야 한다.
- 비지니스 로직
- 예를 들면 회원 엔티티를 하나 생성한 후 엔티티 매니저를 통해 데이터베이스에 CRUD한다.
- save, update, remove, find로 비지니스 로직이 이루어진다.
- JPQL
- JPA를 사용하면 애플리케이션 개발자는 엔티티 객체를 중심으로 개발하고 데이터베이스에 대한 처리는 JPA에 맡겨야함
- JPQL은 엔티티 객체를 대상으로 쿼리
- SQL은 데이터베이스 테이블을 대상으로 쿼리
- 엔티티 매니저 설정
- 결과적으로…
- 개발자는 JPA를 통해 객체의 CRUD구현을 간단하게되었고, 결과값을 매핑해준다.
- JPA 사용법 1. 객체 매핑
-
Chapter 3
- 영속성 관리
- JPA는
- 엔티티와 테이블을 매핑하는 설계하는 부분
- 매핑한 엔티티를 실제 사용하는 부분으로 나뉨
- 엔티티 매니저는 엔티티를 CRUD하는 모든 일을 처리한다.
- JPA는
- 엔티티 매니저 팩토리와 엔티티 매니저
- 애플리케이션은 일반적으로 EntityManagerFactory를 하나만 생성한다.
- Persistence.xml의 정보를 바탕으로 EntityManagerFactory를 생성한다.
- 이렇게 생성된 EntityManagerFactory는 필요할 때마다 엔티티 매니저를 생성한다.
- EntityManagerFactory는 EntityManager를 만드는 공장인데,
- EntityManagerFactory는 생성 비용이 매우 커 한 개만 생성해 공유하여 사용한다.
- EntityManagerFactory는 스레드들이 동시에 접근해도 안전하다.
- EntityManager는 스레드들이 동시에 접근하면 동시성 문제가 발생 → 공유하면 안된다.
- 애플리케이션은 일반적으로 EntityManagerFactory를 하나만 생성한다.
- 영속성 컨텍스트란?
- 영속성 컨텍스트 = 엔티티를 영구 저장하는 환경
- 영속성 컨텍스트는 엔티티 매니저를 생성할 때 만들어지고, 엔티티 매니저를 통해서 영속성 컨텍스트를 접근, 관리할 수 있다.
- 엔티티의 생명주기
- 비영속성
- 엔티티 객체를 생성한 후 아직 저장하지 않은 상태, 데이터베이스와는 전혀 관련이 없다.
- 영속
- 엔티티 매니저를 통해 엔티티를 영속성 컨텍스트에 저장한 상태
- 영속성 컨텍스트가 관리하는 엔티티를 영속상태라 한다.
- 준영속
- 관리하던 영속 상태의 엔티티를 영속성 컨텍스트가 관리하지 않으면 준영속 상태가 된다.;
- 삭제
- 엔티티를 영속성 컨텍스트와 데이터베이스에서 삭제한다.
- 비영속성
- 영속성 컨텍스트의 특징
- 영속성 컨텍스트와 식별자 값
- 영속성 컨텍스트는 테이블의 기본 키와 매핑한 값으로 구분한다.
- 따라서 영속 상태는 식별자 값이 반드시 있어야 한다.
- 엔티티가 데이터베이스에 저장되는 시점
- JPA는 트랜잭션이 Commit되는 순간 영속성 컨텍스트에 저장된 에티티를 데이터베이스에 반영한다.
- 영속성 컨텍스트는 테이블의 기본 키와 매핑한 값으로 구분한다.
- 영속성 컨텍스트가 엔티티를 관리할 때의 장점
- 1차 캐시
- 동일성 보장
- 트랜잭션을 지원하는 쓰기 지연
- 변경 감지
- 지연 로딩
- 영속성 컨텍스트와 식별자 값
- 엔티티 조회
- 영속성 컨텍스트는 캐시를 갖고있는데 이걸 1차 캐시라고 한다.
- 1차 캐시에게 키(@Id)는 식별자 값이고 이 값은 데이터베이스 기본 키와 매핑되어 있다.
- 1차 캐시에서 조회
- find()매서드를 호출하면 1차 캐시 안에서 식별자 값으로 엔티티를 찾는다.
- 만약 1차 캐시에 엔티티가 있으면 데이터베이스를 조회하지않고 엔티티를 조회할 수 있다.
- 데이터베이스 조회
- 만약 find()를 호출했는데 엔티티가 1차 캐시에 없으면 엔티티 매니저는 데이터베이스를 조회햇서 생성한다.
- 그리고 난 뒤 1차 캐시에 저장한 후에 영속 상태의 엔티티를 반환한다.
- 결과적으로 1차 캐시에 저장된 엔티티(영속 상태)는 바로 불러올 수 있기때문에 성능상 이점 + 엔티티 동일성을 보장한다.
- 영속성 컨텍스트는 캐시를 갖고있는데 이걸 1차 캐시라고 한다.
- 엔티티 등록
- 엔티티 매니저는 트랜잭션을 커밋하기 직전까지 내부 쿼리 저장소에 INSERT SQL을 모아둔다.
- 트랜잭션을 커밋할 때 모아둔 쿼리를 데이터베이스에 보내는데 이를 쓰기 지연이라고 한다.
- 자세히
- 트랜잭션을 커밋하면 엔티티 매니저는 영속성 컨텍스트를 플러시한다.
- 플러시는 영속성 컨텍스트의 변경 내용을 데이터베이스에 동기화하는 작업이다
- 이때 CRUD한 엔티티를 데이터베이스에 반영한다.
- 쓰기 지연 SQL저장소에 모인 쿼리를 데이터베이스에 보낸다.
- ⇒ 즉 트랜잭션을 지원하는 쓰기 지연이 가능한 이유
- 트랜잭션을 커밋하면 엔티티 매니저는 영속성 컨텍스트를 플러시한다.
- 엔티티 수정
- SQL 수정 쿼리의 문제점
- SQL을 사용하면 수정 쿼리를 직접 작성해야 한다.
- 프로젝트가 커지고 요구사항이 늘어나면 수정 쿼리도 점점 복잡해지고 커진다.
- 결과적으로 이런 개발의 문제점은 수정 쿼리가 많아지는 것은 물론이고 비지니스 분석을 위해 SQL을 계속 확인해야한다.
- JPA는 엔티티를 어떻게 수정할까?
- JPA를 통해 엔티티를 데이터를 변경하면 자동으로 수정 사항을 반영한다.
- 이를 변경 감지라고 한다.
- JPA는 엔티티를 영속성 컨텍스트에 보관할때, 최초 상태를 저장해둔다. → 이를 스냅샷이라 한다.
- 플러시 시점에서 스냅샷과 엔티티를 비교해서 변경된 엔티티를 찾는다.
- 변경 감지는 영속성 컨텍스트가 관리하는 영속 상태의 엔티티에만 적용된다.
- 변경 감지에서 JPA의 기본 전략은 엔티티의 모든 필드를 업데이트 하는 것이다.
- 이렇게 되면 데이터베이스에 보내는 데이터 전송량이 증가한다는 단점이 있다.
- 하지만 수정 쿼리의 일정함, 재사용성에서의 장점이 있다.
- SQL 수정 쿼리의 문제점
- 엔티티 삭제
- 엔티티를 삭제하기 위해 먼저 해당 엔티티 조회가 필요하다.
- 삭제 대상 엔티티를 넘겨주면 엔티티를 즉시 삭제하는 것이 아니라 엔티티 등록과 비슷하게 쿼리를 쓰기 지연 저장소에 등록한다.
- 플러시
- 플러시는 즉 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영한다는 것을 뜻한다.
- 플러시를 실행하면…
- 변경 감지가 동작하여 기존 데이터와 비교후 변경점을 찾고 수정 쿼리를 만들어 쓰기 지연 SQL 저장소에 등록한다.
- 쓰기 지연 SQL저장소의 쿼리를 데이터 베이스에 전송한다.
- 직접 호출
- 엔티티 매니저의 flush매서드를 직접 호출하여 영속성 컨텍스트를 강제로 플러시
- 거의 사용하지 않는다.
- 트랜잭션 커밋 시 플러시 자동호출
- 데이터베이스에 변경 내용을 SQL로 전달하지 않고 트랜잭션만 커밋하면 어떤 데이터도 데이터베이스에 반영되지 않는다. → 커밋 전 꼭 플러시 호출하여 영속성 컨텍스트 내용을 반영해야한다.
- JPQL 쿼리 실행 시 플러시 자동 호출
- 객체지향 쿼리를 호출할 때도 플러시가 실행된다.
- 엔티티를 영속상태로 만들었다 → 영속성 컨텍스트에는 존재
- 하지만 데이터베이스에는 반영되지 않았다.
- JPQL은 SQL로 변환되어 데이터베이스에서 엔티티를 조회
- 하지만 엔티티는 영속성 컨텍스트에만 존재하고 데이터베이스에는 없다.
- 따라서 쿼리 결과로 조회되지 않는다.
- 따라서 쿼리 실행 직전에 플로시를 해서 변경 내용을 데이터베이스에 반영해야한다.
- 객체지향 쿼리를 호출할 때도 플러시가 실행된다.
- 플러시 모드 옵션
- 플러시 모드는 두 가지가 있다
- 커밋이나 쿼리를 실행할 때 플러시하는 → AUTO
- 커밋할때만 플러시하는 → COMMIT
- 플러시는 이름때문에 영속성 컨텍스트에 보관된 엔티티를 지운다고 착각할 수도 있는데
- 영속성 컨텍스트의 변경내용을 데이터베이스에 동기화하는 것이 플러시이다.
- 플러시 모드는 두 가지가 있다
- 준영속
- 영속 상태의 엔티티가 영속성 컨텍스트에서 분리된 상태
- 준영속 상태의 엔티티는 영속성 컨텍스트가 제공하는 기능을 사용할 수 없다.
- 영속 상태의 엔티티를 준영속 상태로 바꾸는 방법
- .detech매서드 : 특정 엔티티만 준영속 상태로 전환
- 이 매서드를 사용하면 더는 영속성 컨택스트가 관리하지않는다.
- 즉 준영속 상태이다.
- 쓰기 지연 SQL 저장소의 INSERT SQL도 제거되어서 데이터베이스에 저장되지도 않는다.
- .clear : 영속성 컨텍스트를 완전히 초기화
- 영속성 컨텍스트 초기화를 통해 모든 영속성 컨텍스트가 준영속 상태가 된다.
- 준영속 상태이므로 영속성 컨택스트가 지원하는 변경 감지 또한 작동하지 않는다
- .close : 영속성 컨텍스트를 종료
- 영속성 컨텍스는를 종료하면 해당 영속성 컨텍스트가 관리하던 영속 상태의 엔티티가 준영속 상태로 바뀐다.
- .detech매서드 : 특정 엔티티만 준영속 상태로 전환
- 준영속의 특징
- 거의 비영속 상태에 가깝다.
- 식별자 값을 가지고 있다.
- 지연 로딩을 할 수 없다.
- 병합
- 준영속 상태를 다시 영속 상태로 변경하기 위함
- merge()매서드는 준영속 상태의 엔티티를 받아서 정보로 새로운 영속 상태의 엔티티를 반환한다.
- 준영속 병합
- 비영속 병합
- 병합은 비영속 엔티티도 영속 상태로 만들 수 있다.
- 병합은 파라미터로 넘어온 엔티티를 식별자값으로 영속성 컨텍스트에 조회 ⇒ 없으면 데이터베이스에서 조회 ⇒ 없으면 새로운 엔티티를 생성해서 병합한다.
- 병합은 비영속 엔티티도 영속 상태로 만들 수 있다.
- 결과적으로…
- 엔티티 매니저는 엔티티 매니저 팩토리에의해 생성
- 영속성 컨텍스트는 애플리케이션과 데이터베이스 사이의 객체를 보과하는 가상의 데이터베이스라고 보면된다.(1차원 캐시, 동일성 보장, 트랜잭션, 쓰기 지연, 변경 감지, 지연 로딩등의 기능을 제공한다.)
- 영속성 컨텍스트의 관리 유무에 따라 영속 상태를 나뉜다.
- 영속 상태에서 벗어나면 영속성 컨텍스트가 제공하는 기능을 사용할 수 없다.
- 영속성 관리
-
Chapter 4
-
엔티티 매핑
- JPA에서 엔티티-테이블의 정확한 매핑은 매우 중요하다.
- 따라서 JPA에서는 어노테이션를 지원하여 매핑한다.
- 매핑 어노테이션
- 객체-테이블 매핑 어노테이션
- @Entity, @Table
- 기본 키 매핑
- @Id
- 필드와 컬럼 매핑
- @Column
- 연관관계 매핑
- @ManyToOne, @joinColumn
- 객체-테이블 매핑 어노테이션
- JPA에서 엔티티-테이블의 정확한 매핑은 매우 중요하다.
-
@Entity
- JPA를 사용해서 테이블과 매핑할 클래스는 @Entity 어노테이션이 필수
- @Entity가 붙은 클래스는 JPA가 관리하는 것으로 엔티티라고 부른다.
속성 name 기능 엔티티의 이름을 지정한다. (다른 패키지 이름과 중복되지 않도록 설정) 기본값 기본값은 클래스 이름 - 주의 사항
- final class, enum, interface, inner 클래스에는 사용 불가능
- 저장할 필드에 final을 사용하면 안된다.
- 기본 생성자는 필수이다.
-
@Table
속성 name 기능 엔티티와 매핑할 테이블을 지정한다. 기본값 엔티티 이름을 사용 -
다양한 매핑 사용
- 기본 키 매칭
- 기본키를 직접 할당하려면 @Id만 사용하면 되고, 자동 생성 전략을 사용하려면 @Id에 @GeneratedValue를 추가하고 원하는 키 생성 전략을 선택하면 된다.
- 기본 키 직접 할당 전략
-
IDENTITY 전략
- 기본 키 생성을 데이터베이스에 위임하는 전략
- @Id 컬럼 을 비워두면 데이터베이스가 순서대로 값을 채워준다.
- @GeneratedValue어노테이션을 사용하고 식별자 생성 전략을 선택해야한다.
-
SEQUENCE 전략
- 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트이다.
- SEQUENCE전략은 이 시퀀스를 사용해서 기본 키를 생성한다.
- 이 전략은 Oracle, PostgreSQL등에서 사용할 수 있다.
- 시퀀스 사용 코드는 IDENTITY전략과 같지만 내부 동작 방식은 다르다
- 데이터베이스 시퀀스를 사용해서 식별자 조회
- 조회한 식별자를 엔티티에 할당
- 엔티티를 영속성 컨텍스트에 저장
- 이후 트랜잭션 커밋해서 플러시가 일어나면 엔티티를 데이터베이스에 저장
-
@SequenceGenerator
속성 name initialValue allocationSize 기능 엔티티와 매핑할 테이블을 지정한다. DDL 생생시에만 사용됨 DDL이 처음 시작하는 수를 지정한다. 시퀀스 한 번 호출에 증가하는 수 기본값 엔티티 이름을 사용 1 50 - JPA는 시퀀스에 접근하는 횟수를 줄이기 위해 @SequenceGenerator.allocationSize를 사용한다.
- allocationSize값이 50이면 시퀀스를 한 번에 50 증가시킨 다음에 1~50까지는 메모리에서 식별자를 할당한다.
- 그리고 그 값 즉 50을 넘기면 100까지 증가시킨다.
-
- 테이블 전략
- Table전략은 키 생성 전용 테이블을 만들고 여기에 이름과 값으로 사용할 컬럼을 만들어 시퀀스를 흉내내는 전략이다.
- Table전략은 시퀀스 대신에 테이블을 사용한다는 것만 제외하면 SEQUENCE전략과 내부 동작방식이 같다.
- @TableGenerator
- Table전략은 값을 조회하면서 SELECT, 다음 값으로 증가시키기 위해 UPDATE를 사용한다
- SEQUENCE에 비해 데이터베이스에 한 번 더 통신하는 단점이 있다.
- AUTO 전략
- Generation.AUTO는 선택한 데이터베이스 방언에 따라 IDENTITY, SEQUENCE, TABLE 전략 중 하나를 자동으로 선택한다.
- 장점으로
- 데이터베이스를 변경해도 코드를 수정할 필요가 없다
- 키생성 전략이 확정되지 않은 개발 초기, 프로토타입 개발시 편리
- 기본 키 매칭
-
결과적으로…
- 엔티티의 식별자 값은 영속성 컨텍스트가 구분하는 기준이므로 매우 중요하다.
- 직접할당 : 영속성 컨텍스트 호출전 직접 식별자 값을 할당
- SEQUENCE : 데이터베이스를 통해 시쿼스 값으 갖고와 영속성 컨텍스트를 통해 저장
- TABLE : 데이터베이스 시퀀스 생서용 테이블에서 식별자 값을 획득한 후 영속성 컨텍스트에 저장
- IDENTITY : 데이터베이스에 엔티티를 저장해서 식별자 값을 획득한 후 영속성 컨텍스트에 저장한다.
- 위와 같은 방법으로 @Id값을 생성할 수 있다.
- 영한센세가 권장하는 식별자 선택 전략
- 먼저 데이터베이스 기본키 조건을 만족할 것
- NULL값을 허용하지 않는다.
- UNIQUE하다.
- 변하면 안된다.
- 기본키 선택 전략
- 자연키
- 비지니스에 의미가 있는 키
- 주민등록번호, 전화번호, 이메일
- 대리키
- 시퀀스, 시리얼, 키생성 테이블
- 자연키
- 영한 센세는 자연키보다는 대리키를 권장한다.
- 이유는…
- 예를들면 전화번호는 유일하지만 변경 가능성이 있고, 주민등록번호조차 여러가지 이유로 변경될 수 있다.
- JPA는 모든 엔티티에 일관된 방식으로 대리키 사용을 권장한다.
- 이유는…
- 먼저 데이터베이스 기본키 조건을 만족할 것
- 엔티티의 식별자 값은 영속성 컨텍스트가 구분하는 기준이므로 매우 중요하다.
-
-
Chapter 5
- 연관 관계 매핑의 핵심 키워드
- 방향
- 방향에는 단방향, 양방향이 있다.
- 다중성
- 다대일, 일대다, 일대일, 다대다 다중성이 있다
- 연관 관계의 주인
- 객체는 양방향 연관관계로 만들면 연관관계의 주인을 정해야 한다.
- 방향
- 단방향 연관 관계
- Member - Team은 다대일 관계이다.
- 객체 연관 관계
- 회원과 팀은 단방향 연관 관계이다.
- Member은 Team필드를 통해 팀을 알지만 Team은 Member을 알 수 없다.
- 테이블 연관 관계
- 하지만 테이블 연관 관계에서는 FK인 TEAM_ID를 통해 회원과 팀은 양방향으로 접근가능한 양방향 관계를 맺는다.
- 객체 연관 관계와 테이블 연관 관계의 가장 큰 차이점
- 객체의 참조를 통한 연관관계는 언제나 단방향이다.
- 양방향으로 만들기 위해서는 반대쪽 필드에도 참조를 추가해줘야한다.
- 이렇게 양쪽을 서로 참조하는 것을 양방향 연관관계라고 한다.
- 하지만 정확히는 서로 다른 단방향 관계 2개이다.
- 객체 연관 관계 vs 테이블 연관 관계
- 객체는 참조를 통해 연관 관계를 맺는다.
- 테이블은 PK로 연관 관계를 맺는다.
- 이 두 특징은 비슷해보이지만 다른 특징을 가진다.
- 참조는 단방향, 외래키는 양방향 연관 관계
- 순수한 객체 연관 관계
- 객체 참조를 통해 연관 관계를 탐색할 수 있는데 이것을 객체 그래프 탐색이라고 한다.
- 테이블 연관 관계
- 데이터베이스는 외래키를 통해 연관 관계를 탐색 할 수 있다.
- 객체 관계 매핑
- JPA를 통한 객체 관계 매핑
- 연관 관계 매핑의 핵심 키워드
-
Chapter 6
- 다양한 연관관계 매핑
- 엔티티의 연관관계에서는 다중성과 방향을 고려해야 한다.
- 엔티티 간의 관계가 1:1, 1:N인지 고려하고
- 엔티티 간의 참조하는 방향이 단방향인지 양방향인지 고려해야 한다.
- 다중성
- N:1 @ManyToOne
- 1:N @OneToMany
- 1:1 @OneToOne
- N:M @ManyToMany
- 실무에서는 보통 다대일, 일대다 관계를 가장 많이 사용 다대다 관계는 실무에서 사용하지 않는다
- 단방향, 양방향
- 데이터베이스의 테이블 관점에서는 외래 키FK 하나로 양방향 쿼리가 가능하다.
- 반면 객체는 참조용 필드를 가지고 있는 객체만 연관된 객체를 조회할 수 있다.
- 한쪽만 참조하는 것을 단방향, 양쪽 전부 참조하는 것을 양방향 관계라고 한다.
- 연관관계의 주인
- 연관관계를 관리하는 것은 외래키FK이다.
- 보통 외래키를 가진 엔티티가 관리하기 효율적이므로 연관관계 주인으로 선택된다.
- 연관관계 주인은 mappedBy속성을 사용하지 않는다.
- 단방향 연관관계에서는 주인이 명확하기때문에 필요없다.
- 양방향 연관관계에서는 필요하다.
- 연관관계 주인은 mappedBy속성을 사용하지 않는다.
- 다대일
- 다대일 관계의 반대 방형은 일대다 관계이다.
- 데이터베이스 관점에서 테이블의 일, 다 관계에서 외래키는 항상 다쪽에 있다.
- 따라서 객체 양방향 관계에서 연관관계의 주인은 항상 다쪽이다.
- 다대일 단방향
- ex) Member - Team 객체의 관계
- 다대일 양방향
- 양방향은 외래키가 있는 쪽이 연관관계의 주인이다.
- 양방향은 항상 서로를 참조해야 한다.
- 양쪽에 다 작성할 경우 무한루프에 빠지므로 조심해야한다.
- 일대다
- 일대다 관계는 엔티티를 하나 이상 참조할 수 있으므로 자바 컬렉션 중에 하나를 사용해야 한다.
- 일대다 단방향
- 한쪽은 참조하지만 다른 쪽이 참조하지않으면 둘의 관계는 단방향이다.
- 단방향 관계를 매핑할 때는 @JoinColumn을 명시해야한다.
- 일대다 단방향 매핑의 단점
- 본인 테이블에 FK가 있으면 저장과 연관관계 처리를 INSERT 한번으로 끝낼 수 있지만, 다른 테이블에 있다면 연관관계 처리를 위해 UPDATE를 추가로 실행해야 함
- 일대다 단방향 매핑보다는 다대일 양방향 매핑을 사용하자!!
- 일대다 양방향
- 일대다 양방향 매핑은 존재하지 않는다.
- 대신 다대일 양방향 매핑을 사용해야 한다.
- 양방향 매핑에서 @OneToMany는 연관관계의 주인이 될 수 없다 → 다대일 관계에서는 항상 다 쪽에 FK가 있다. ⇒ 따라서 연관관계 주인은 @ManyToOne을 사용하는 다 쪽이고, 여기에는 mappedBy 속성이 없다.
- 일대다 단방향
- 일대다 관계는 엔티티를 하나 이상 참조할 수 있으므로 자바 컬렉션 중에 하나를 사용해야 한다.
- 일대일
- 일대일 관계는 양쪽이 서로 하나의 관계만 가진다 → 따라서 주 테이블, 대상 테이블 상관없이 FK를 가진다.
- 그러면 누가 PK를 가질지만 정하면된다.
- 우리는 주테이블에 외래 키를 두는 것이 좋을 것이다.
- JPA를 통해 주테이블에 외래 키를 편리하게 매핑할 수 있기 때문
- 그러면… @OneToOne을 사용하고, 참조되는 쪽에는 mappedby속성을 추가하면 됨
- 다대다
- RDB에서는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없다.
- 그래서 일대다-다대일로 관계를 풀어 낸다
- 객체는 컬렉션을 사용해 다대다 관계를 만들 수 있다.
@ManyToMany를 통해 다대다 관계를 편리하게 매핑할 수 있다.
- 하지만 테이블은 불가능하다.
- 다대다 양방향
- @ManyToMany를 통해 다대다 관계를 편리하게 매핑할 수 있지만, 결국에는 엔티티를 하나 더 만들어 @OneToMany, @ManyToOne으로 풀어 사용해야한다.
- 실무에서는 컬럼을 추가하고, 약간의 변형이 필요하기 때문이다.
- Member-Order-Product이런 식으로
- 실무에서는 컬럼을 추가하고, 약간의 변형이 필요하기 때문이다.
- @ManyToMany를 통해 다대다 관계를 편리하게 매핑할 수 있지만, 결국에는 엔티티를 하나 더 만들어 @OneToMany, @ManyToOne으로 풀어 사용해야한다.
- 엔티티의 연관관계에서는 다중성과 방향을 고려해야 한다.
- 다양한 연관관계 매핑
-
Chapter 7
- 프록시와 연관관계 관리
- 프록시
- 엔티티를 조회할 때 연관된 엔티티들이 항상 사용되는 것이 아니다.
- Member-Team이 연관된 상태에서 memberId로 조회할때
- 만약 member정보만 필요한 상태에서 memberId를 통해 Team까지 조회하는 것은 비효율적일 뿐만아니라 조회된 불필요한 데이터들이 사용되지도 않는다.
- 따라서, JPA에서는 Team이라는 엔티티가 사용될때까지 데이터베이스에서 조회를 미룬다.
- 이걸 지연로딩이라고 한다.
- 그리고 지연로딩을 구현하기 위해서 JPA는 가짜객체인 프록시 객체를 갖고온다.
- 프록시의 특징
- 프록시 클래스는 실제 클래스를 상속 받아서 만들어지므로 실제 클래스와 겉 모양이 같다.
- 사용하는 입장에서는 해당 객체가 진짜인지 프록시인지 구분하지 않고 사용 가능
- 프록시 객체는 실제 객체에 대한 참조를 보관한다.
- 프록시 객체의 초기화
- 데이터베이스를 조회해서 실제 엔티티 객체를 생성하는것을 프록시 객체의 초기화라 한다.
- 프록시 객체는 처음 사용할 때 한 번만 초기화된다.
- 프록시 객체를 초기화해도 실제 엔티티로 바뀌는 것은 아니다
- 프록시 객체는 원본 엔티티를 상속받은 객체이므로 타입 체크 시에 주의해서 사용해야 한다.
- 프록시와 식별자
- 엔티티를 프록시로 조회할 때 식별자 값을 파라미터로 전달하는데 프록시 객체는 이 식별자 값을 보관한다.
- 연관관계를 설정할 때는 식별자 값만 사용하므로 프록시를 사용하면 데이터베이스 접근 횟수를 줄일 수 있다
- 프록시 확인
- JPA가 제곡하는 .isLoaded()메서드를 사용하면 프록시 인스턴스의 초기화 여부를 확인할 수 있다.
- 즉시 로딩과 지연 로딩
- 즉시 로딩
- 엔티티를 조회할 때 연관된 엔티티도 함께 조회한다.
- 대부분 JPA 구현체는 즉시로딩을 최적화 하기 위해 가능하면 조인 쿼리를 사용한다.
- 지연 로딩
- 연관된 엔티티를 실제 사용할 때 조회한다.
- 프록시 객체를 통해 데이터가 실제 사용될 때까지 데이터 로딩을 미룬다.
- JPA 기본 패치 전략
- 연관된 객체간의 사용빈도에 따라 로딩 방법을 나눌 수 있다. (연관된 두 객체가 자주 사용되면, 즉시 로딩-가끔 사용되면 지연 로딩)
- 객체의 연관된 엔티티가 하나면 즉시 로딩, 컬렉션이면 지연 로딩을 사용한다.
- 컬렉션을 로딩하는 것은 비용이 크기 때문
- 컬렉션의 로딩
- FetchType.EAGER 사용 시 주의점
- 컬렉션을 하나 이상 즉시 로딩하는 것은 권장하지 않는다.
- 서로 다른 컬렉션이 조인되면서 N*M이 되면서 앱 성능을 저하 시킬수 있기때문
- 컬렉션 즉시 로딩은 항상 OUTER JOIN을 사용한다.
- JPA는 일대다 관계를 즉시 로딩할 때 항상 외부조인을 사용한다.
- FetchType.EAGER 사용 시 주의점
- 즉시 로딩
- 프록시와 컬렉션 래퍼
- 엔티티를 지연 로딩하면 프록시 객체를 사용해서 지연 로딩을 수행하지만 컬렉션은 컬렉션 래퍼가 지연 로딩을 처리해준다.
- 프록시
- 영속성 전이 : CASCADE
- 특정 엔티티와 연관된 엔티티 함께 영속 상태로 만들고 싶으면 영속성 전이 기능을 사용하면 된다.
- JPA에서는 CASCADE 옵션으로 영속성 전이를 제공한다.
- 영속성 전이 : 저장
- 영속성 전이는 연관관계를 매핑하는 것과는 관련이 없다.
- 영속성 전이 : 삭제
- CascadeType.REMOVE로 설정하고 부모 엔티티를 삭제하면 연관된 자식 엔티티도 함께 삭제된다.
- 따라서 부모 엔티티를 삭제하면 자동으로 DELETE SQL을 3번 실행하여 연관된 자식을 모두 삭제한다.
- CascadeType.REMOVE로 설정하고 부모 엔티티를 삭제하면 연관된 자식 엔티티도 함께 삭제된다.
- CASCADE의 종류
- ALL, PERSIST, MERGE, REMOVE, REFRESH, DETACH등이 있다.
- 고아 객체
- JPA는 부모 엔티티와 연관관계가 끊긴 자식 엔티티를 자동 삭제하는 기능을 제공한다.
- 부모 엔티티의 컬렉션에서 자식 엔티티의 참조만 제거하면 고아 객체가 된다.
- orphanRemoval 옵션으로 인해 컬렉션에서 엔티티를 제거하면 데이터베이스의 데이터도 삭제된다. → 이를 통해 참조하지 않는 고아객체를 삭제할 수 있다.
- 영속성 전이 + 고아 객체, 생명 주기
- 자식을 저장하려면 부모에 등록만 하면 된다.
- 자식을 삭제하려면 부모에서 삭제하기만 하면 된다.
- 정리
- JPA 구현체들은 객체 그래프를 마음껏 탐색할 수 있도록 지원하는데 이때 프록시 기술을 사용한다.
- 연관된 객체를 즉시 로딩하는 바업ㅂ, 지연 로딩하는 방법이 있다.
- 객체를 저장하거나 삭제할 때 연관된 객체도 함께 저장하거나 삭제할 수 있다.
- 또한, 연관관계가 끊어진 고아 객체를 자동으로 삭제하는 기능도 있다.2
- 프록시와 연관관계 관리
-
Chapter 8
- 값 타입
- JPA의 데이터 타입 구분
- 엔티티 타입
- 엔티티 타입은 @Entity로 정의하는 객체
- 값 타입
- 값 타입은 int, Integer, String같은 자바 기본 타입이나 객체
- 값 타입 구분
- 기본 값 타입
- 자바 기본 타입
- 패퍼 타입
- 임베디드 타입(복합 값 타입)
- 임베디드 타입은 JPA에서 사용자가 직접 정의한 값 타입이다.
- 컬렉션 타입
- 기본 값 타입
- 엔티티 타입
- 기본값 타입
- Member엔티티는 id라는 PK 값을 가지고 생명주기도 있다
- 하지만 Member 엔티티에 name, age같은 속성은 회원 엔티티가 제거되면 같이 제거된다.
- Member엔티티는 id라는 PK 값을 가지고 생명주기도 있다
- 임베디드 타입
- JPA에서는 직접 새로운 값타입을 정의할 수 있다. 그것을 임베디드 타입이라고 한다.
- 임베디드 타입도 int, String처럼 값 타입이다.
- 새로 정의한 값 타입(=임베디드 타입)은 재사용 가능성이 높고, 응집도도 높다.
- 임베디드 타입을 사용하려면 2가지 애노테이션이 필요하다.
- @Embeddable : 값 타입을 정의하는 곳에 표시
- @Embedded : 값 타입을 사용하는 곳에 표시
- 임베디드 타입은 기본 생성자가 필수이다.
- 테이블 매핑
- 임베디드 타입과 무관하게 매핑하는 테이블은 같다.
- 덕분에 객체와 테이블을 아주 세밀하게 매핑하는 것이 가능하다.
- 연관관계
- 임베디드 타입은 값 타입을 포함하거나 참조할 수 있다.
- 임베디드 속성 재정의
- @AttributeOverride를 사용해서 임베디드 타입을 재정의 할 수 있다.
- 값 타입과 불변 객체
- 임베디드 타입을 여러 엔티티에서 공유하면 위험하다.
- 값만 변경하길 원하지만 참조하고 있는 원치 않는 객체의 데이터도 수정되어 버린다.
- 그에 대한 근본적인 해결책이 불변 객체이다.
- 불변 객체
- 위와 같은 부작용을 해결하기 위한 방법은 객체를 불변하게 만들면 부작용을 원천 차단할 수 있다.
- 따라서 값 타입은 가능하면 불변 객체로 설계해야 한다.
- Integer, String은 자바가 제공하는 대표적인 불변 개체이다.
- JPA의 데이터 타입 구분
- 값 타입
-
JPA를 공부하며 참고할것
-
외래키가 있는 곳을 연관관계 주인으로 정하라!
- 외래키가 있는 곳을 연관관계 주인으로 정하지 않으면 다른 테이블이 업데이트 되므로 유지보수가 어렵고, 별도의 업데이트 쿼리가 발생해 성능에도 악영향을 미친다.
-
엔티티에는 가급적 @Setter를 사용하지 말자!
- @Setter는 너무 많은 변경 포인트를 만들어내 유지보수를 어렵게한다.
- @Setter때문에 엔티티의 변경 추적이 어려워진다.
- 실무에서는 @Setter대신 변경 지점이 명확한 비지니스 매서드를 별도로 만든다.
- @Setter는 너무 많은 변경 포인트를 만들어내 유지보수를 어렵게한다.
-
값타입은 변경 불가능하게 설계하라!
- 먼저 @Setter를 제거하자
- 생성자에 값의 초기화를 우임해서 변경 불가능한 클래스를 만들자
- 이때 기본 생성자를 public , protected로 설정해야 한다. (public 보다는 protected를 사용)
- 직접 정의한 값 타입은 자바의 기본 타입이 아니라 객체 타입이다.
- 자바의 기본 타입에 값을 대입하면 값 복사를 하지만
- 객체 타입은 참조 값을 복사하기 때문에 불변객체로 설계하여 임의의 오류를 막는다.
-
모든 연관관계는 지연로딩으로 설정하라!
- 즉시로딩은 예측을 어렵게하여 오류를 일으키고, N + 1문제를 발생시킨다.
- 따라서 실무에서는 모든 연관관계를 지연로딩으로 설정해야한다. (@OneToOne, @ManyToOne 관계는 기본이 즉시 로딩이므로 .LAZY명시를 필요로한다.)
- 연관된 엔티티를 함께 조회해야할때는 Fetch join, Entity Graph, BatchSize를 사용한다.
-
컬렉션은 필드에서 초기화 하라!
- 컬렉션은 필드에서 초기화하는 것이 안전하다. (null문제)
@OnetoMany(mappedBy = "feed")private List<Comment> comments = ***new Arraylist<>();*** //엔티티정의에서 초기화
-
API를 만들때는…
- 엔티티를 외부로 반환하지마라!
- 엔티티가 수정된다면, API도 변해버린다.
- DTO를 사용해서 필요없거나 민감한 정보의 노출을 차단하자.
- 페치 조인으로 쿼리 수를 최적화 하거나 @BatchSize로 최적화
- 엔티티를 외부로 반환하지마라!
-
JPA의 영속성
- EntityManagerFactory
- EntityManagerFactory는 생성하는데 많은데 많은 자원을 소모하기 때문에 싱글톤 패턴으로 애플리케이션 전체에서 공유
- EntityManagerFactory는 쓰레드들이 동시에 접근해도 안전하다.
- EntityManager
- EntityManager는 쓰레드들이 공유하게 되면 동시성 문제가 발생하기때문에 공유해선 안된다.
- Entity
- 비영속
- 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
- 영속
- 영속성 컨텍스트에 관리되는 상태
- 준영속
- 영속성 컨텍스트에 저장되었다가 분리된 상태
- 영속성 컨텍스트의 이점
- 동일성보장, 트랜잭션을 지원하는 쓰기 지연, 변경감지
- 플러시
- 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영한다.
- 커밋 실행 시 자동 실행, 직접 호출, JPQL 쿼리 실생 시 장동 실행으로 플러시 할 수 있다.
- 비영속
- EntityManagerFactory
-
JPA 페이징과 정렬
- Page
- 추가 count 쿼리 결과를 포함
- page: 현재 페이지, 0부터 시작한다.
- size: 한 페이지에 노출할 데이터 건수
- sort: 정렬 조건을 정의한다.
- 추가 count 쿼리 결과를 포함
- List
- 추가 count 쿼리 없이 결과만 반환
- Pageable 파라미터를 통해 페이징과 정렬을 적용
- Sort 파라미터를 통해 정렬만 적용
- Page
-
벌크성 수정 쿼리
- 벌크성 수정-삭제 쿼리는 @Modifying 어노테이션을 사용한다.
- @Modifying(clearAutomatically = false(기본값)/true)를 사용해서 영속성 컨텍스트를 초기화해야한다.
- 위 옵션없이 findbyId로 다시 조회하면 영속성 컨텍스트에 과거 값이 남아서 문제가 될 수 있다.
- 벌크성 연산을 할때는…
- 영속성 컨텍스트에 엔티티가 없는 상태에서 벌크 연산을 먼저 수행
- 영속성 컨텍스트에 엔티티가 있는 상태에서 벌크 연산을 수행하면 수행 직후 영속성 컨텍스트를 초기화
-
@EntityGraph
- JPQL 없이 페치 조인을 사용할 수 있다
- 사실상 페치 조인의 간편 버전이다.
- LEFT OUTER JOIN을 사용한다.
-
연관 관계
- 테이블은 연관관계를 외래키를 통해 찾고, 객체는 참조를 통해 찾는다.
- 따라서 테이블과 객체는 간극이 존재한다.
- 객체의 연관관계
- Member → Feed (단방향)
- Feed → Member (단방향) 조합으로 양방향처럼 보이게 만든다
- 테이블의 연관관계
- Member ↔︎ Feed (양방향)
- 객체는 단방향 2개를 양방향처럼 보이게한다. 하지만 이는 단방향 2개이다.
- 테이블은 외래키 하나로 두 테이블의 연관관계를 관리한다.
- 따라서 관계를 갖는 객체 중 하나는 외래키를 관리해야한다.
- 연관관계의 주인
- 관계를 갖는 객체 중 하나를 연관관계의 주인으로 지정
- 주인은 등록, 수정과 같은 관리가 가능하다.
- 주인이 아닌 쪽은 Read만 가능하다.
- 주인이 아닌 쪽은 mappedBy속성 사용
- 외래키가 있는 곳을 객체의 주인으로 정해라
-
프록시 엔티티
- 실제 클래스를 상속 받아서 만들어지기 때문에 타입이나 특징이 같다.
- 프록시 객체를 호출하면 프록시 객체는 실제 객체의 매서드 호출
- 프록시 객체는 처음 사용할 때 한번만 초기화한다.
- 지연 로딩시 프록시로 조회한다.
- 실무에서는 지연로딩만 사용한다.
- 즉시 로딩은 예상치 못한 SQL을 발생시키고, JPQL에서 N + 1 문제를 일으킨다.
-
영속성 전이
- CASCADE 타입을 통해 연관된 객체를 관리할 수 있다.
- 예를 들면
- 고아 객체
- 부모 엔티티와 연관관계가 끊어진 자식엔티티
- orphanRemoval = true로 사용하면 자동으로 삭제
- CascadeType.ALL + orphanRemoval = true 두 옵션을 사용하면
- 부모 엔티티를 통해서 자식의 생명주기를 관리할 수 있음
- 고아 객체
-
JPA의 데이터 타입 분류
- 엔티티 타입
- @Entity로 정의하는 객체이다.
- 데이터가 변해도 식별자로 추적할 수 있다.
- 값 타입
- Integer, String처럼 단순히 값으로 사용하는 자바 기본 타입이나 객체
- 식별자가 없고 값만 있으므로 변경 시 추적이 불가능하다.
- 값 타입 분류
- 기본값 타입
- 자바 기본 타입 : int, double
- 래퍼 클래서 : Integer, Long
- String
- 임베디드 타입
- 직접 정의한 값 타입을 JPA에서는 임베디드 타입이라고 함
- 주로 기본 값 타입을 모아서 만들어서 복합 값 타입이라고도 함
- @Embeddable: 값 타입을 정의하느 곳에 표시
- @Embedded: 값 타입을 사용하는 곳에 표시
- 기본 생성자 필수
- 임베디드 타입의 장점
- 재사용, 높은 응집도
- 값 타입의 실제 인스턴스인 값을 공유하는 것은 위험하다
- 대신 값을 복사해서 사용한다.
- 따라서 불변객체로 만들어 수정할 수 없게 만들면 부작용을 차단할 수 있다.
- 기본값 타입
- 엔티티 타입
-
JPQL (JAVA PERSISTENCE QUERY LANGUAGE)
- 테이블이 아닌 객체를 대상으로 질의하는 객체 지향 쿼리이다.
- SQL을 추상화해서 데이터베이스의 SQL에 의존하지 않는다.
- 문법
- 엔티티와 속성은 대소문자를 구분한다.
- JPQL 키워드는 대소문자를 구분하지 않는다.
- 엔티티 이름을 사용한다. (테이블 이름은 사용하지 않는다.)
- 별칭은 선택이 아닌 필수이다.
- 프로젝션
- SELECT절을 통해 조회할 대상을 지정하는 것
- 조인
-
내부, 외부, 세타조인이 있다.
-
Fetch Join (페치 조인)
- SQL 조인 종류 X
- JPQL에서 성능 최적화를 위해 제공하는 기능
- 연관된 엔티티나 컬렉션을 SQL 한번에 조회하는 기능
-
Entity Fetch Join
- 회원을 조회하면서 연관된 팀도 함께 조회
//JPQLselect m from Member m join fetch m.team//SQLSELECT M.*, T.* FROM MEMBER M INNER JOIN TEAM T ON M>TEAM_ID = T.ID
- Fetch Join과 일반 Join의 차이점
- 일반 조인은 실행시 연관된 엔티티를 함께 조회하지 않음
- SELECT절에 지정한 엔티티만 조회할 뿐
- 페치 조인을 사용할 때만 연관된 엔티티도 함께 조회(객체 그래프를 SQL 한번에 조회하는 개념)
- Fetch Join의 특징과 한계
- 둘 이상의 컬렉션은 페치 조인을 할 수 없다.
- 컬렉션을 페치 조인하면 페이징을 사용할 수 없다.
- 연관된 엔티티를 SQL한번에 조회하여 성능 최적화
- 실무에서 글로벌 로딩 전략은 항상 지연로딩한다.
-
-
벌크 연산 주의
- 벌크 연산은 영속성 컨텍스트를 무시하고 데이터베이스에 직접 쿼리
- 벌크 연산을 먼저 실행
- 벌크 연산 수행 후 영속성 컨텍스트 초기화
- 벌크 연산은 영속성 컨텍스트를 무시하고 데이터베이스에 직접 쿼리
-