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

2022. 5. 13. 01:51Web/Spring

반응형

date: 2022-03-10T13:57:15.537Z

김영한님의 '자바 ORM 표준 JPA 프로그래밍' 서적을 읽고 정리한 글 입니다.

엔티티 매니저 팩토리와 엔티티 매니저

엔티티 매니저 팩토리

  • 엔티티 매니저를 만드는 공장.
  • 한 개만 만들어서 애플리케이션 전체에서 공유하도록 설계됨. (생성 비용이 큼)
  • 여러 스레드가 동시에 접근해도 안전

엔티티 매니저

  • emf.createEntityManager() 을 통해 생성.
  • 생성 비용이 거의 들지 않는다.
  • 여러 스레드가 동시에 접근하면 동시성 문제 발생.
  • 데이터베이스 연결이 꼭 필요한 시점까지 커넥션을 얻지 않는다.

영속성 컨텍스트

  • 엔티티를 영구 저장하는 환경.
  • em.persist(entity) 는 엔티티 매니저를 사용해서 엔티티를 영속성 컨텍스트에 저장하는 것이다.
  • 엔티티 매니저를 통해서 영속성 컨텍스트에 접근할 수 있고 영속성 컨텍스트를 관리할 수 있다.

엔티티의 생명주기

  • 비영속(new/transient): 영속성 컨텍스트와 전혀 관계 없는 새로운 상태
  • 영속(managed): 영속성 컨텍스트에 관리되는 상태
  • 준영속(detached): 영속성 컨텍스트에 저장되었다가 분리된 상태
  • 삭제(removed): 삭제된 상태

비영속

  • member 객체를 생성만 하고 엔티티 매니저에 아무것도 안 넣은 상태다.
  • JPA와 전혀 관계없는 상태라고 할 수 있다.

영속

  • em.persist() 로 객체를 넣어주면, 엔티티 매니저 안에 있는 영속성 컨텍스트에 엔티티가 들어가면서 영속 상태가 된다.
  • 영속 상태라는 것은 영속성 컨텍스트에 의해 관리된다는 뜻이다.

준영속

  • 영속 상태의 엔티티를 영속성 컨텍스트가 관리하지 않게되면 준영속 상태가 된다.
  • em.detach(), em.close(), em.clear() 를 호출하면 준영속 상태가 된다.

삭제

  • 엔티티를 영속성 컨텍스트와 데이터베이스에서 삭제한다.
  • em.remove()

영속성 컨텍스트의 장점

1차 캐시

  • 영속화를 하면 @Id 로 매핑한 식별자를 키로, 엔티티 객체가 값이 되어 1차 캐시에 저장된다.
  • 엔티티를 조회할 때 1차 캐시에 있으면 그 데이터를 반환하고, 없다면 DB에서 조회한 후 1차 캐시에 저장한 후에 영속 상태의 엔티티를 반환한다.

동일성 보장

Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");

System.out.println(a == b);    // true
  • 동일한 엔티티를 반복해서 호출해도 영속성 컨텍스트는 1차 캐시에 있는 같은 엔티티 인스턴스를 반환한다.

트랜잭션을 지원하는 쓰기 지연

  • 엔티티 매니저는 트랜잭션을 커밋하기 직전까지 내부 쿼리 저장소에 쿼리를 차곡차곡 모아둔다.
  • 트랜잭션을 커밋할 때 모아둔 쿼리를 DB에 보낸다.
  • 이 기능을 잘 활용하면 모아둔 등록 쿼리를 DB에 한 번에 전달해서 성능을 최적화 할 수 있다.

변경 감지

SQL 수정 쿼리의 문제점

  • 수정 쿼리 개수가 많아진다.
  • 비즈니스 로직을 분석하기 위해 SQL을 계속 확인해야 한다. (비즈니스 로직이 SQL에 의존하게 된다)

JPA 변경 감지

  • 엔티티의 변경사항을 DB에 자동으로 반영하는 기능 (Dirty Checking)
  • 엔티티를 영속성 컨텍스트에 보관할 때, 최초 상태를 복사해서 저장해둔다. (스냅샷)
  • flush 시점에 스냅샷과 엔티티를 비교해서 변경된 엔티티를 찾는다.
  • 변경 감지는 영속 상태의 엔티티에만 적용된다.
  • JPA의 기본 전략은 엔티티의 모든 필드를 업데이트한다. (@DynamicUpdate 으로 수정된 데이터만 동적으로 업데이트 할 수 있음.)
    • 애플리케이션 로딩 시점에 수전 쿼리를 미리 생성해두고 재사용 할 수 있다.
    • DB에 동일한 쿼리를 보내면 DB는 이전에 파싱된 쿼리를 재사용할 수 있다.

상황에 따라 다르지만 칼럼이 대략 30개 이상이 되면 @DynamicUpdate 를 사용한 동적 수정 쿼리가 더 빠르다고 한다.

플러시

영속성 컨텍스트의 변경 내용을 DB에 반영한다.

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

  1. 직접 호출
  2. 트랜잭션 커밋
  3. JPQL 쿼리 실행 (JPQL을 중간에 실행했는데 아직 커밋을 하지 않아서 persist() 한 내용을 불러올 수 없으면 문제가 생길 수 있다. 그래서 JPQL 실행 시에는 자동으로 무조건 flush() 를 날려버린다.)

플러시 모드 옵션

  • FlushModeType.AUTO
    • 기본값
    • 커밋이나 쿼리를 실행할 때 플러시 된다.
  • FlushModeType.COMMIT
    • 커밋할 때만 플러시 된다.
    • 중간에 실행하는 JPQL 쿼리가 앞과는 전혀 다른 데이터를 써서 굳이 당장 플러시할 필요가 없을 때 사용하면 된다.

준영속

영속 상태의 엔티티가 영속성 컨텍스트에서 분리된 상태.

준영속 상태로 만드는 방법

  1. em.detach()
  2. em.clear()
  3. em.close()

준영속 상태의 특징

  • 거의 비영속 상태에 가깝다. (영속성 컨텍스트가 제공하는 어떠한 기능도 동작하지 않는다)
  • 식별자 값을 가지고 있다.
  • 지연 로딩을 할 수 없다.
반응형