Spring/Spring Data JPA

[자바 ORM 표준 JPA 프로그래밍] 3. 영속성 관리

noahkim_ 2023. 11. 28. 11:57

김영한 님의 "자바 ORM 표준 JPA 프로그래밍" 책을 정리한 포스팅 입니다.

 

1. 영속성 컨텍스트

  • 엔티티 객체의 영속화 작업을 관리하는 작업 영역
  • 1차 캐시, 해당 엔티티 관련 내부 쿼리 저장소

 

역할

역할 설명
엔티티 상태 관리
엔티티를 데이터베이스와 동기화함 (EntityManager에 의해 관리됨)
트랜잭션 범위
영속성 컨텍스트는 트랜잭션 단위로 엔티티를 관리
- 트랜잭션이 시작되면 활성화되고, 종료 시 커밋 또는 롤백됨
캐시 기능
이미 로드된 엔티티는 다시 데이터베이스 조회 없이 재사용됨.

 

동작

동작 설명
자동 플러시
트랜잭션 커밋 전에 영속성 컨텍스트에서 변경된 데이터를 자동으로 데이터베이스에 반영.
수동 플러시
영속성 컨텍스트의 변경 사항을 강제로 데이터베이스에 반영. (entityManager.flush())

 

예제) 자동 플러시

더보기
@DataJpaTest
@TestPropertySource(locations = "classpath:application-test.yml")
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@ContextConfiguration(classes = Main.class)
public class FlushTest {

    @Autowired
    private UserRepository userRepository;

    @Test
    @Order(1)
    @Transactional
    @Commit
    void auto_flush_test1() {
        userRepository.save(new User("auto flush"));
        
        // entityManager.flush() 호출 안해도 자동으로 해줌
    }

    @Test
    @Transactional
    void auto_flush_test2() {
        assertNotNull(userRepository.findByName("auto flush"));
        
        // 조회 성공
    }
}

 

장점

장점 설명
성능 최적화
1차 캐시와 자동 플러시 기능을 통해 불필요한 데이터베이스 접근을 최소화.
엔티티 관리
엔티티의 생명주기를 자동으로 관리하여 데이터베이스와 객체 간의 동기화를 효율적으로 처리.

 

2. 엔티티 생명주기

 

상태 설명
New / Transient (비영속)
엔티티 객체가 생성된 시점의 상태.
영속성 컨텍스트와 전혀 연관이 없습니다.
데이터베이스에 반영되지 않은 상태입니다.
Managed (영속)
영속성 컨텍스트에 저장된 상태.
엔티티가 영속성 컨텍스트의 관리 하에 있으며, 변경 사항이 자동으로 동기화됩니다.
Detached (준영속)
영속성 컨텍스트에서 저장되었다가 분리된 상태.
더 이상 관리되지 않습니다.
Removed (삭제) 엔티티가 삭제된 상태.
데이터베이스에서 삭제될 예정

 

예제

더보기
@DataJpaTest
@TestPropertySource(locations = "classpath:application-test.yml")
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@ContextConfiguration(classes = Main.class)
public class EntityStatusTest {

    @Autowired
    private UserRepository userRepository;

    @PersistenceContext
    private EntityManager entityManager;

    @Test
    @Transactional
    void testEntityStatus() {
        // New / Transient 상태
        User newUser = new User("test");
        assertTrue(!entityManager.contains(newUser));

        // Managed 상태
        userRepository.save(newUser);
        assertTrue(entityManager.contains(newUser));

        // Detached 상태
        entityManager.detach(newUser);
        assertTrue(!entityManager.contains(newUser));

        // Removed 상태
        userRepository.delete(newUser);
        assertTrue(!entityManager.contains(newUser));
        assertNull(userRepository.findByName("test"));
    }
}

Removed 상태

  • personRepository.delete(person)를 호출하면 해당 엔티티는 removed 상태에 들어감
  • 실제로 데이터베이스에서 삭제됩니다

 

3. 상태 메서드

메서드명 설명 동작 방식 특징 / 주의사항
find() 엔티티 조회 1. 1차 캐시 조회
2. 없으면 DB 조회 후 1차 캐시에 저장
식별자(PK)로 조회
persist() 엔티티 등록 1. 영속성 컨텍스트 등록
2. 1차 캐시에 저장
3. 쓰기 지연 저장소에 INSERT 쿼리 저장
쓰기 지연 (flush 또는 커밋 시 DB 반영)
flush() 변경사항 반영 1. 스냅샷과 비교
2. 변경 감지 시 UPDATE 쿼리 생성
3. 내부 쿼리 저장소에 전송
UPDATE 쿼리 시 모든 필드 업데이트
- 재사용성 ⬆️ / 데이터 전송량 ⬆️
- @DynamicUpdate: 수정될 필드만 업데이트
Flush Mode

- FlushModeType.AUTO: 커밋 전, JPQL 실행 전
- FlushModeType.COMMIT: 커밋 전
detach() 준영속 전환 엔티티를 영속성 컨텍스트에서 분리
entityManager.contains()가 false 반환
merge() 준영속 → 영속 전환 새 영속 인스턴스 생성 및 반환
인수는 그대로 준영속 상태 유지
반환 객체 사용해야 함
remove() 엔티티 삭제 영속 상태 → 삭제 상태로 전환
삭제 쿼리는 flush/커밋 시 실행
준영속 상태의 엔티티에는 사용 불가
clear() 컨텍스트 초기화 모든 영속 엔티티를 준영속으로 전환
flush 없이 수행 시 변경사항 손실
close() 컨텍스트 종료 EntityManager 종료 재사용 불가 상태가 됨

 

예제) flush() - @DynamicUpdate

더보기
@Entity
public class User { ... }
update user set name=?, nickname=? where id=?
  • 기본적으로 모든 필드를 대상으로 업데이트됨

 

@Entity
@DynamicUpdate
public class User { ... }
update user set name=? where id=?
  • @DynamicUpdate 사용 시, 수정될 필드만 업데이트 됨

 

예제) merge()

더보기
@Test
@Transactional
void merge() {
    User detached = new User("auto flush");

    User merged = entityManager.merge(detached);

    assertFalse(entityManager.contains(detached));
    assertTrue(entityManager.contains(merged));
}
  • 새로운 영속 상태의 객체가 반환됨
  • 반환된 객체로 작업해야 함

 

출처