Tech/Spring

[Spring JPA] 준영속 상태, 더티 체킹, 변경감지, 병합

0m1n 2023. 4. 17. 22:30
728x90
반응형

준영속 상태

  • 영속 상태의 엔티티가 영속성 컨텍스트에서 분리된 것
  • 영속성 컨텍스트가 제공하는 기능 사용 불가
  • em.detach(entity)
    • 특정 엔티티만 준영속 상태로 전환
    • 1차 캐시에서 빠진다라고 생각하면 쉬움
  • em.clear()
    • 영속성 컨텍스트 완전히 초기화
    • 테스트 케이스 작성시 사용
  • em.close()
    • 영속성 컨텍스트를 종료

준영속 엔티티

  • 영속성 컨텍스트가 더는 관리하지 않는 엔티티(DB에 한번 갔다 와서 식별자가 존재)
  • 변경 감지, 병합을 통해 준영속 엔티티를 수정할 수 있다.

변경 감지 기능

@Transactional
void update(Item itemParam) {
 Item findItem = em.find(Item.class, itemParam.getId());
 findItem.setPrice(itemParam.getPrice());
}
  • 영속성 컨테스트에서 해당 id를 다시 조회한후 데이터를 수정
  • 조회한 findItem은 영속상태
  • 트랜잭션 안에서 엔티티를 다시 조회, 변경할 값 선택 트랜잭션 커밋 시점에 변경 감지(Dirty Checking)이 동작해서 데이터베이스에 UPDATE SQL 실행

더티 체킹(Dirty Checking)

  • 상태 변경 검사
  • JPA에서는 트랜잭션이 끝나는 시점에 변화가 있는 모든 엔티티 객체를 데이터베이스에 자동으로 반영
  • JPA에서는 엔티티를 조회하면 해당 엔티티의 조회 상태를 스냅샷으로 만듦
  • 트랜잭션이 끝나는 시점에 이 스냅샷과 비교해서 변경이 있으면 Update query 발생
  • Dirty Checking 대상은 영속성 컨텍스트가 관리하는 엔티티에만 적용
  • 준영속, 비영속 상태의 엔티티는 Dirty Checking 대상에 포함되지 않음
    • Detech된 엔티티 - 준영속
    • DB에 반영되기 전 처음 생성된 엔티티 - 비영속
    • 값을 변경해도 데이터베이스에 반영되지 않음

병합

	//itemParam: 파리미터로 넘어온 준영속 상태의 엔티티
	@Transactional
	void update(Item itemParam) {
	  Item mergeItem = em.merge(itemParam);
	}

  1. merge(itemParam)를 실행
  2. 파라미터로 넘어온 준영속 엔티티의 식별자로 1차캐시에 조회(만약 1차 캐시에 없다면 DB에 조회후 1차캐시에 저장)
  3. 조회한 영속 엔티티(item)를 준영속 엔티티(itemParam) 값으로 수정
  4. 영속상태인 mergeItem을 반환

코드로 표현시

  @Transactional
  public Item update(Long itemId, Item itemParam) {
  		Item findItem = itemRepository.findOne(itemId);
      	findItem.setName(itemParam.getName());
        ...
        return findItem;
  }

병합 단점

  • em.merge(itemParam) 이 코드의 경우 파라미터에 객체를 모두 보냄
    • 특정 속성 변경 불가, 객체 모든 속성 변경

결론

  • 준영속 엔티티를 변경할때는 항상 변경 감지를 사용
    • 컨트롤러에서 엔티티 생성 금지 → 파라미터나 DTO 이용해 서비스 계층(트랜잭션)에 데이터 전달
    • 트랜잭션이 있는 서비스 계층에서 영속 상태의 엔티티를 조회하고 엔티티의 데이터를 직접 변경
    • 트랜잭션 커밋 시점에 변경 감지 실행
728x90
반응형