JPA - 영속성

노력하는 배짱이·2022년 7월 20일
0

JPA

목록 보기
2/9

영속성 컨텍스트 (PersistenceContext)

  • 엔티티(Entity)를 영구 저장하는 환경
  • 영속성 컨텍스트는 논리적인 개념이며, 엔티티 매니저(EntityManager)를 통해서 영속성 컨텍스트에 접근
  • EntityManager를 생성하면 영속성 컨텍스트가 하나 생성이 됨 -> 1:1
  • 또는, 여러 EntityManager 가 하나의 영속성 컨텍스트에 접근이 가능 -> N:1
  • Entity를 저장(persist), 조회(find)를 하면, DB가 아닌 영속성 컨텍스트에 엔티티를 저장 및 조회를 함

엔티티(Entity)의 생명주기

  • 비영속 (new / transient)
    영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
    즉, 객체(Entity)를 처음 생성했을 때

  • 영속 (managed)
    영속성 컨텍스트에 관리되는 상태
    저장 : entityManager.persist(entity)
    조회 : entityManager.find(entity) -> 영속성 컨텍스트에 데이터가 없는 경우

  • 준영속 (detached)
    영속성 컨텍스트에 저장되었다가 분리된 상태

  • 삭제 (removed)
    삭제된 상태

비영속

  • 객체를 생성하고, EntityManager를 통해 아무것도 안한 상태
// Member Entity 정의
@Entity
class Member(
    @Id
    val id: Long = 0L,
    var name: String
)
// 객체 생성
val member: Member = Member(id = 1L, name = "member1")

영속

  • 객체(Entity)와 EntityManager 를 생성하고,
    EntityManager 를 통해 영속성 컨텍스트에 넣은 상태
val emf: EntityManagerFactory = Persistence.createEntityManagerFactory(persistenceUnitName)

// EntityManager 생성
val em: EntityManager = emf.createEntityManager()
val tx: EntityTransaction = em.transaction
tx.begin()

// 객체 생성 - 비영속 상태
val member: Member = Member(id = 1L, name = "member1")
    
// 객체 저장 - 영속 상태
em.persist(member)

준영속

  • 엔티티(Entity)를 영속성 컨텍스트에서 분리
em.detach(member)

삭제

  • 엔티티(Entity)를 영속성 컨텍스트에서 삭제
em.remove(member)

영속성 컨텍스트 이점

  1. 1차 캐시
  2. 동일성(Identity) 보장
  3. 트랜잭션을 지원하는 쓰기 지연 (Transctional Write-Behind)
  4. 변경 감시 (Dirty Checking)
  5. 지연 로딩 (Lazy Loading)

1차 캐시

  • 영속성 컨텍스트는 내부에 1차 캐시가 존재
  • Entity를 조회(find)하는 경우, 1차 캐시에 존재하는지 확힌 후 반환
  • 1차 캐시에 존재하지 않는 경우, DB에서 조회 해서 1차 캐시에 넣고 해당 값을 반환
// 객체 생성 - 비영속 상태
val member: Member = Member(id = 1L, name = "member1")

// 객체 저장 - 영속 상태
em.persist(member)

// Entity 조회
val findMember: Member = em.find(Member::class.java, 1L)

영속 엔티티 - 동일성(Identity) 보장

  • 컬렉션에서 동일한 객체를 조회 후 비교하면 동일한 것 처럼 JPA도 동일성을 보장한다
    -> 같은 트랜잭션 안에서 조회한다는 전제
  • 1차 캐시로 반복 가능한 읽기(REPEATABLE READ) 등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공

트랜잭션 격리 수준 (Isolation Level)
1. READ UNCOMMITED : 커밋되지 않은 데이터도 읽기
2. READ COMMITED : 커밋된 데이터 읽기
3. REPEATABLE READ : 반복 가능한 읽기 -> 동일한 트랜잭션 내에서는 동일한 결과
4. SERIALIZABLE : 직렬화 가능

val findMember1: Member = em.find(Member::class.java, 1L)
val findMember2: Member = em.find(Member::class.java, 1L)
        
val result = findMember1 == findMember2 // true

엔티티 등록 - 트랜잭션을 지원하는 쓰기 지연

  • 영속성 컨텍스트 내부에 쓰기 지연 SQL 저장소가 존재
    생성, 수정, 삭제와 같은 SQL은 커밋 직전까지 저장소에 있다가, 커밋이 된 후 Flush 되면서 DB에 해당 SQL 전송
// EntityManager 생성
val em: EntityManager = emf.createEntityManager()
val tx: EntityTransaction = em.transaction
// 트랜잭션 시작 - 엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 함
tx.begin()

// 객체 생성 - 비영속 상태
val member1: Member = Member(id = 1L, name = "member1")
val member2: Member = Member(id = 2L, name = "member2")
        
// 객체 저장 - 영속 상태
em.persist(member1)
em.persist(member2)
        
// 이때 DB에 Insert SQL를 수행
tx.commit()

엔티티 수정 - 변경 감시 (Dirty Checking)

  • JPA 는 변경 감지 기능으로 엔티티를 변경할 수 있는 기능을 제공함
    커밋되는 시점에 Flush 가 호출이 되는데,영속성 컨텍스트 내부의 엔티티와 스냅샷을 비교
  • 변경된 엔티티가 있으면 UPDATE SQL를 쓰기 지연 SQL 저장소에 생성 후 DB에 반영하고 커밋을 함
// 객체 생성 - 비영속 상태
val member: Member = Member(id = 1L, name = "member1")

// 객체 저장 - 영속 상태
em.persist(member)

// Entity 조회
val findMember: Member = em.find(Member::class.java, 1L)
        
// Entity 수정
findMember.name = "member222"

// 호출할 필요가 없음
em.persist(findMember)

엔티티 삭제

  • 엔티티(Entity) 수정 매커니즘이랑 동일하며, 커밋 시점에 DELETE 쿼리가 나감
// Entity 조회
val findMember: Member = em.find(Member::class.java, 1L)

// Entity 삭제
em.remove(findMember)

플러시

  • 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영 (동기화)

  • 플러시는 트랜잭션이 커밋될 때 발생
    -> 트랜잭션이라는 작업 단위가 중요

  • 플러시는 변경 감지가 있을 때 발생하며, 수정된 엔티티 쓰기 지연 SQL 저장소에 등록

  • 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송(등록, 수정, 삭제 쿼리)

  • 영속성 컨텍스트를 비우지 않음 -> 1차 캐시 데이터 유지

영속성 컨테스트를 플러시하는 방법

  1. entityManager.flush() : 직접 호출
    : 1차 캐시에 있는 데이터는 그대로 유지
  2. 트랜잭션 커밋 : 플러시 자동 호출
  3. JPQL 쿼리 실행 : 플러시 자동 호출

플러시 모드 옵션

em.flushMode = FlushModeType.AUTO 

FlushModeType

  • FlushModeType.AUTO : 커밋이나 쿼리를 실행할 대 플러시 (기본값)
  • FlushModeType.COMMIT : 커밋할 때만 플러시

준영속 상태

  • 영속 상태에서 준영속 상태로 변화
    영속 상태 2가지
    -> persist
    -> find 해서 조회했을 때, 영속성 컨텍스트에 없는 경우

  • 영속 상태의 엔티티가 영속성 컨텍스트에서 분리(Detached)된 상태

  • 영속성 컨텍스트가 제공하는 기능을 사용 못함

준영속 상태로 만드는 방법

  1. entityManager.detach(entity)
    특정 엔티티만 준영속 상태로 전환
    -> 커밋 전에 detach 하면 관련 SQL이 DB에 보내지지 않음
  2. entityManager.clear()
    : 영속성 컨텍스트를 완전히 초기화
  3. entityManager.close()
    : 영속성 컨텍스트를 종료

참고 : 인프런 강의[자바 ORM 표준 JPA 프로그래밍 - 기본편]

0개의 댓글