기록용 블로그

JPA 영속성(persistence) 본문

Back-End/Java

JPA 영속성(persistence)

tram 2018. 9. 19. 01:55

JPA를 공부하면 계속 나오는 영속성에 대해서 정리.

엔티티 매니저

JPA의 엔티티 매니저(EntityManager)는 엔티티 저장, 수정, 삭제 조회 등 엔티티와 관련된 모든 일을 처리한다.

엔티티 매니저는 엔티티 매니저 팩토리를 통해 생성되는데, 팩토리는 아래와 같이 생성할 수 있다.

팩토리는 일반적으로 하나의 DB를 사용하는 경우 하나의 Factory만 생성한다.

EntityManagerFactory emf = Persistence.createEntityManagerFactory("note");

엔티티 매니저는 팩토리를 통해 필요할 때 마다 생성할 수 있다.

EntityManager em = emf.createEntityManager();
- EntityManagerFactory
  - 생성 비용이 크다, 어플리케이션 전체에서 하나를 가지고 공유한다.
  - 여러 스레드 동시 접근에 대한 문제 발생 하지 않음.
- EntityManager
  - 팩토리를 통해 생성, 엔티티 매니저를 생성하는 비용은 거의 들지 않는다.
  - 여러 스레드 동시 접근 시 동시성 문제 발생.

팩토리에서 엔티티 매니저를 생성하고, 엔티티 매니저는 트랜잭션이 시작될 때 DB 커넥션을 획득해서 DB와 관련된 일들을 처리하는 방식.


영속성 컨텍스트란?

엔티티를 영구 저장하는 환경. 엔티티 매니저를 통해서 영속성 컨텍스트에 접근하거나 관리를 할 수 있다.

EntityManager em = emf.createEntityManager();
em.persist(member);

위와 같은 상황에서 persist() 메소드는 엔티티 매니저를 통해 회원 엔티티를 영속성 컨텍스트상에 저장하는데, 저장 이후부터 영속성 컨텍스트가 멤버 엔티티를 관리하게 된다.

여러 엔티티 매니저가 같은 영속성 컨텍스트에 접근할 수 있음. 어떤 경우인진 모르겠지만 트랜잭션 전파가 일어나는 경우에 해당하지 않을까 싶다.

영속성 컨텍스트는 아래와 같은 특징들을 가진다.

영속성 컨텍스트와 식별자 값

엔티티를 식별자 값(@Id로 테이블 기본키와 매핑한 값)으로 구분. 따라서 영속 상태는 반드시 식별 값이 필요하다. 없으면 예외 발생

영속성 컨텍스트와 데이터베이스 저장

트랜잭션을 커밋하는 순간 영속성 컨텍스트에 저장된 엔티티를 데이터베이스에 반영한다.

영속성 컨텍스트가 엔티티를 관리하면 아래와 같은 장점 제공

  • 1차 캐시

영속성 컨텍스트 내부에 키 @Id, 엔티티를 값으로 가지는 Map 형태의 캐시 제공.

  • 동일성 보장

1차 캐시의 엔티티 인스턴스 반환을 통해 동일성 보장 제공.

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

트랜잭션 커밋 전까지 발생한 INSERT(persist)를 내부 쿼리 저장소에 저장한 후 커밋 시 모아둔 쿼리를 전송한다.

  • 변경 감지

엔티티를 영속성 컨텍스트에 보관할 때 스냅샷 생성. 플러시 시점에 스냅샷과 엔티티를 비교해 변경된 엔티티를 찾고 수정 쿼리를 자동으로 생성해 쓰기 지연 SQL 저장소에 보낸 뒤 커밋 시 반영.(영속상태의 엔티티만 적용.)

  • 지연 로딩

프록시 객체를 통한 지연 로딩 제공. 나중에 더 살펴보자.


엔티티 생명 주기



엔티티는 아래와 같은 생명 주기를 가진다.

비영속 - New

엔티티 객체를 생성한 경우, 객체 상태라 DB나 영속성 컨텍스트와 전혀 상관 없는 상태

Member member = new Member("안녕!");

영속 - Managed

엔티티 매니저를 통해 엔티티를 영속성 컨텍스트에 저장한 상태

em.persist(member);

준영속 - Detached

영속성 컨텍스트가 관리하던 영속 상태의 엔티티를 영속성 컨텍스트가 관리하지 않는 상태.

em.detach(member); //엔티티를 영속성 컨텍스트에서 분리, 준영속 상태
em.close(); //영속성 컨텍스트가 닫힌 경우

삭제 - Removed

엔티티를 영속성 컨텍스트, DB에서 삭제.

em.remove(member);


플러시(flush())

영속성 컨텍스트의 변경 내용을 데이터베이스에 반영하는 것. 플러시를 실행하면 아래와 같은 일이 발생한다.

  1. 변경 감지가 동작, 수정 쿼리를 만들어 쓰기 지연 SQL 저장소에 등록
  2. 쓰기 지연 SQL 저장소 쿼리를 DB에 전송(등록, 수정, 삭제)

영속성 컨텍스트를 플러시하는 방법은 3가지 방법이 있다.

  1. em.flush()
  2. 트랜잭션 커밋
  3. JPQL 커리 실행 시 플러시 자동 호출.

JPQL이나 Criteria 같은 객체지향 쿼리 호출 시 플러시 실행. 아래와 같은 경우 쿼리 결과가 조회되지 않는 문제때문에 플러시를 실행한다.

em.persist(memberA);
em.persist(memberB);

query = em.createQuery("select m from Member m", Member.class);
List<Member> members = query.getResultList();

엔티티 매니저에 직접 플러시 모드를 지정할 수 있다.

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

별도 설정하지 않는 경우 AUTO로 동작하는데, 성능 최적화를 위해 COMMIT을 사용하는 경우도 있다고 함.

em.setFlushMode(FlushModeType.COMMIT);

참고

  • 자바 ORM 표준 프로그래밍
Comments