JPA 영속성 관리와 연관 관계 관리

Song Chae Won·2023년 5월 24일
0

스프링부트와 AWS

목록 보기
17/20
post-thumbnail

2023-05-23 EFUB 백엔드 세션을 듣고 정리한 내용입니다.

영속성 관리

EntityManagerFactory와 EntityManager

  • 엔티티 매니저는 엔티티 CRUD와 관련된 모든 일을 처리한다.
  • 엔티티 매니저 팩토리에서 엔티티 매니저를 생성한다.
  • 엔티티매니저는 데이터베이스 연결이 필요할 때(예: 트랜잭션 시작)마다 커넥션풀에서 커넥션을 획득한다.

엔티티의 생명주기

  • 영속성 컨텍스트 : 엔티티를 영구 저장하는 환경
  • 엔티티 매니저로 엔티티를 저장/조회하면 엔티티 매니저는 영속성 컨텍스트에 엔티티를 보관
    ➡️ 이때 “엔티티를 저장”한다는 것
    ➡️ DB에 곧바로 저장한다 (x)
    영속성 컨텍스트에 보관한다(o)
  • 영속성 컨텍스트는 논리적인 개념에 가깝다!

영속성 컨텍스트

  • 4가지 상태가 존재한다.
  1. 비영속 (new/transient)
    : 영속성 컨텍스트와 무관한 상태. 엔티티를 생성하고 아직 저장하지 않았을 경우 비영속에 해당됨.

  2. 영속(managed)
    : 엔티티 매니저를 통해 엔티티를 영속성 컨텍스트에 저장한 상태.
    영속성 컨텍스트에 의해 관리되는 상태.

  3. 준영속(detached)
    : 영속성 컨텍스트에 의해 관리되다가 영속성 컨텍스트에서 분리되어 ,더 이상 영속성 컨텍스트에 의해 관리되지 않는 상태

  4. 삭제(removed)
    : 엔티티를 영속성 컨텍스트와 데이터베이스에서 삭제한 상태

1차 캐시

  • 영속성 컨텍스트는 내부에 1차 캐시를 들고 있다.
  • em.persist()를 실행하면 엔티티가 영속성 컨텍스트 내부의 1차 캐시에 저장된다.
    이때 엔티티는 식별자 값(@Id로 테이블의 PK와 매핑한 값)으로 구분된다.

엔티티 조회

  • 엔티티매니저의 find 메서드로 엔티티 조회 가능
    ex) em.find(엔티티클래스의 타입, 조회할 엔티티의 식별자 값)
  • 먼저 1차 캐시에서 식별자값으로 엔티티를 찾고, 만약 없으면 DB에서 조회한다.

동일성 보장

  • 영속성 컨텍스트는 성능상 이점과 엔티티의 동일성을 보장한다.
  • 동일성 : 실제로 인스턴스가 같을 때
    동등성 : 실제 인스턴스가 달라도 인스턴스가 가진 값이 가진 경우

영속성 컨텍스트의 이점: 쓰기 지연

EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin(); // 트랜잭션 시작
em.persist(fubyA); // 영속성 컨텍스트 내부 1차 캐시에 저장 (영속화)
transaction.commit(); // 트랜잭션 커밋.
//커밋하는 순간 데이터베이스에 INSERT SQL을 보냄.

영속성 컨텍스트의 이점: 엔티티 등록

1) persist()로 엔티티를 영속화
2) 엔티티는 영속성 컨텍스트 1차 캐시에 저장 && JPA가 엔티티를 분석해 INSERT 쿼리를 생성하여 쓰기 지연 SQL 저장소에 쌓아둠
3) 트랜잭션을 커밋하면 쓰기 지연 SQL 저장소에 있
는 쿼리들이 flush 되면서 DB에 날아간다.
4) DB 트랜잭션 커밋.

영속성 컨텍스트의 이점: 엔티티 수정, 변경 감지

  • 변경 감지 : 엔티티의 변경사항을 DB에 자동으로 반영하는 기능

1) 트랜잭션을 커밋
2) 영속성 컨텍스트 내부에서 flush 호출
3) 엔티티랑 스냅샷을 비교하여 변경된 엔티티 찾음
* 스냅샷 : 1차 캐시 안에 들어오는 최초의 상태를 본떠둔 것
4) 변경된 것은 UPDATE 쿼리를 생성하여 쓰기 지연 SQL 저장소에 보냄
5) 쓰기 지연 저장소의 SQL을 DB에 보냄
6) DB 트랜잭션 커밋.

영속성 컨텍스트의 이점: 엔티티 삭제

1) em.remove(memberA)로 삭제할 엔티티를 넘겨주면 DELETE 쿼리를 쓰기 지연 SQL 저장소에 등록
이때 memberA 엔티티는 영속성 컨텍스트에서 제거됨
2) 트랜잭션을 커밋하면 flush 호출하여 DB에 DELETE 쿼리 전달

FLUSH(플러시)

  • 플러시 : 영속성 컨텍스트의 변경 내용을 DB에 반영하는 것
  • 플러시를 실행하면
    1. 변경 감지가 동작하여 영속성 컨텍스트에 있는 모든 엔티티와 스냅샷을 비교하여 수정된 엔티티를 찾음
    2. 수정된 엔티티는 UPDATE 쿼리를 만들어 쓰기 지연 SQL 저장소에 쌓아둠
    3. 쓰기 지연 SQL 저장소의 쿼리를 DB에 전송
  • 영속성 컨텍스트를 플러시 하는 방법
  1. em.flush() 메서드 호출
    : 테스트나 다른 프레임워크와 JPA를 같이 사용할 때를 제외하고는 거의 사용하지 않는다.
  2. 트랜잭션 커밋 시 플러시 자동 호출
    : JPA는 트랜잭션을 커밋할 때 플러시를 자동으로 호출한다.
  3. JPQL 쿼리 실행시 플러시 자동 호출
    : JPA는 JPQL을 실행할 때도 플러시를 자동으로 호출한다.

지연 로딩

즉시 로딩과 지연 로딩

즉시 로딩

  • 엔티티를 조회할 때 연관된 엔티티도 함께 조회
  • @ManyToOne(fetch = FetchType.EAGER)로 설정

지연 로딩

  • 연관된 엔티티까지 함께 조회할 필요는 없을 때 사용

  • @ManyToOne(fetch = FetchType .LAZY)로 설정한다.

  • @ManyToOne, @OneToOne과 같이 @XXXToOne 어노테이션들은 기본이 즉시 로딩(EAGER) 이다.

  • 실무에서는 즉시 로딩을 지양하도록 한다.

  • 대부분의 JPA 구현체는 즉시로딩을 최적화하기 위해 가능하면 조인 쿼리를 사용하는데 이때 조인이 중첩되면 오히려 쿼리의 성능을 저하시킬 수 있다.

  • 즉시로딩은 JPQL에서 N+1 문제를 일으킨다.

N+1 문제
: 객체를 데이터베이스에서 불러올 때 연관관계 객체를 불러오기 위한 1개의 쿼리가 아닌, N개의 쿼리가 생성되는 문제이다.

영속성 전이(CASCADE)

: 특정 엔티티를 영속 상태로 만들 때 연관된 엔티티도 함께 영속 상태로 만들고 싶을 때 사용, 엔티티를 영속화할 때 연관된 엔티티도 함께 영속화하는 편리함을 제공하는 기능일 뿐 연관관계 매핑과 관련이 없음

(ex) 부모 엔티티를 영속 컨텍스트에 저장할 때 자식 엔티티도 함께 영속 컨텍스트에 저장하고 싶을 때

CASCADE 종류

All : 생명주기를 다 맞추려 할 때
PERSIST : 저장할 때만 영속성 전이를 이용할 때
REMOVE : 삭제
MERGE : 병합
REFRESH : refresh
DETACH : detach

영속성 전이를 사용하는 상황

  • [1:N] 연관관계인 엔티티에 항상 사용하는 것은 아님

  • 특정 엔티티를 관리하는 엔티티가 하나일 때
    영향을 주는 부모 엔티티가 여럿이면 영속성 전이를 사용할 이유가 없음
    자식의 소유자가 부모 하나일 때, 단일 엔티티에 종속적일 때만 영속성 전이 사용의 의미가 있음

  • 생명주기가 동일할 때 사용

고아 객체

부모 엔티티와 연관관계가 끊어진 자식 엔티티, 참조가 제거된 엔티티

고아 객체 제거 : 고아 객체를 자동으로 삭제

  • 참조하는 곳이 하나인 엔티티에만 사용
  • 자식의 부모 엔티티가 하나일 때 사용
  • @OneToOne, @OneToMany인 경우에 사용
  • 부모를 제거할 때 자식도 함께 제거되므로 CascadeType.REMOVE를 설정했을 때와 같은 효과를 가짐

orphanRemoval = true로 설정

@Entity
public class Parent {@OneToMany(mappedBy=“parent”, orphanRemoval = true)
private List<Child> childList = new ArrayList();
}

em.remove(parent); 실행하면 child 객체들도 삭제됨

영속성 전이 + 고아 객체

CascadeType.ALL + orphanRemoval=true
부모 엔티티를 통해서 자식의 생명주기를 관리

@Valid

  • 특정 데이터가 유효한지, 잘못된 내용을 담고 있는지 확인하는 단계
  • 값과 값의 형식이 설정한 제약 조건에 부합하는지 검증
  • 주로 사용자 또는 다른 서버의 request에 담긴 데이터를 대상으로 수행됨
  • 검증을 통해 잘못된 데이터로 동작을 수행하지 않도록 막음

Validation 진행 방법

Java Bean Validation

Validation 라이브러리에서 제공하는 annotation을 이용

Spring Validator 인터페이스 구현을 통한 Validation

Validator 인터페이스를 구현하는 클래스를 생성하고 그 클래스를 스프링 빈으로 등록해서 사용

제약 조건 Annotation
@NotNull : null을 허용하지 않음
@NotEmpty : null과 “”를 허용하지 않음
@NotBlank : null과 “”과 “ “을 허용하지 않음
@AssertTrue : 값이 참이어야 함, null도 허용함
@Pattern : 특정 정규표현식을 만족해야 함
@Size : 지정된 경계(포함) 사이의 값을 가져야 함
@Min : 지정된 최솟값보다 크거나 같은 값을 가져야 함
@Max : 지정된 최댓값보다 작거나 같은 값을 가져야 함

profile
@chhaewxn

0개의 댓글