JPA

영속성 컨텍스트

salmon16 2023. 8. 26. 16:45

EntityManagerFactory와 EntityManager

  • EntityManagerFactory는 고객의 요청이 들어올 때 마다 (thread가 생성될 때마다) EntityManager를 생성한다.
  • EntityManager은 내부적으로 DB 커넥션 pool을 사용해서 DB에 접근한다.
  • EntityManagerFactory
    • JPA는 EntityManagerFactory를 만들어야 한다. 
    • application loading 시점에 DB당 딱 하나만 생성되어야 한다. 
    • WAS가 종료되는 시점에 EntityManagerFactory를 닫는다. ( 내부적으로 DB 커넥션을 반환한다.
  •  EntityManager
    • Transaction 단위를 수행할 때마다 생성한다.(고객의 요청이 들어오면 생성 후 끝나면 닫는다.)
    • thread간 공유하면 안된다.
    • Transaction후엔 반드시 Transaction을 닫는다.(DB 커넥션을 반환)
  • EntityTransaction
    • 데이터를 변경하는 모든 작업은 반드시 Transaction안에서 이루어 져야한다.
      • 단순 조회는 상관이 없다.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello"); // Factory생성
EntityManager em = emf.createEntityManager(); // Manager get

EntityTransaction tx = em.getTransaction(); // Transaction get
tx.begin(); // Transaction 시작

try {
    Book book = new Book(); // 데이터 생성
    book.setName("JPA"); // 데이터 설정
    book.setAuthor("ABC"); 
    em.persist(book); // 데이터 영속성 컨텍스트에 저장
    tx.commit(); // Transaction 수행
} catch (Exception e) {
    tx.rollback(); // 에러 발생시 Transaction rollback
} finally {
    em.close(); // Manager닫기
}

emf.close(); // Factory 닫기

 

엔티티 매니저 팩토리와 엔티티 매니저의 관계를 보자

엔티티 매니저 팩토리에서 앤티티 매니저를 생성해 준다 그리고 엔티티 매니저를 사용하여 DB와 연결된다.

영속성 컨텍스트란  엔티티를 영구 저장하는 환경이라는 뜻이다.

엔티티 매니저를 통해 영속성 컨텍스트에 접근한다.

 

엔티티의 생명주기는 4가지가 있다

1. 비영속 :영속성 컨텍스트와 전혀 관계가 없는 상태이다 (새로만들어진 ) persist 전이다.

객체만 생성

Member member = new Member();
member.setName("userA");

2. 영속 : 영속성 컨텍스트에서 관리되는 상태이다 persist 후

persist만 했다고 DB에 SQL이 나가지 않는다.

Transaction이 commit이 되는 시점에 SQL이 DB로 보내진다.

em.persist(member);

3. 준영속 : 영속성 컨텍스트에 저장되어 있다가 분리된 상태이다. defach

em.detach(memeber);

4. 삭제 : 삭제된 상태이다. DB에 삭제를 요청한 상태 remove 

em.remove(member);

 

영속성 켄텍스트를 이용하면 다양한 이점을 얻을 수 있는다

1. 1차 캐시사용

2. 동일성 보장

3. 트랜젝션을 지원하는 쓰기 지연

4. 변경 감지

5. 지연 로딩

 

하나씩 살펴보자

1차 캐시 사용

엔티티가 영속 상태가 되면 1차 캐시에 (키, 객체)로 등록이 된다.

이를 영속성 컨텍스트라고 생각해도 된다. 

1차 캐시를 이용하면 DB에 접근 없이 1차 캐시에서 조회가 가능하다.

또 DB에 저장된 데이터를 조회하면 1차 캐시에 업로드된 후 객체가 반환된다.

동일성 보장

Member findMember1 = em.find(Member.class, "1");
Member findMember2 = em.find(Member.class, "1");
System.out.println(findMember1 == findMember2);

위 코드에서 true가 print 된다 즉 동일 키에서 얻은 객체 findMember1, findMember2는 같은 인스턴스이다.

 

주의!! 1차 캐시로 반복 가능한 읽기(REPEATABLE READ) 등급의 트랜잭 션 격리 수준이어야 성립한다.

 

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

entityManager.persist(memberA);
entityManager.persist(memberB);

transaction.commit();

위 코드에서 persist일 때 DB에 쿼리를 날리는 게 아니라 

쓰기 지연 SQL 저장소에 쿼리를 보관 후 commit할 때 DB에 쿼리를 보낸다. 

 

변경 감지

 commit 전에 update를 해야 할 거 같지만 1차 캐시 안에는 스냅샷이라는 DB에서 처음 올라올 때의 상태 또는 처음 persist할 때의 상태를 저장해 두었다가 commit() 시 현제 상태와 스냅샷을 비교해서 update SQL을 쓰기 지연 SQL 저장소에 보내고 DB에 SQL을 보낸다.

엔티티 삭제

Member findMember = em.find(Member.class, "1");
em.remove(findMember);

엔티티를 DB에서 삭제하려면 그냥 remove메서드를 사용하면 된다.

 

플러시

플러시란 영속성 컨텍스트의 변경내용을 데이터 베이스에 반영하는 작업이다.

플러시가 발생하면 영속성 컨텍스트의 변경을 감지해서 update SQL을 생성해서 쓰기 지연 SQL 저장소에 SQL을 등록하고 쓰기 지연 SQL 저장소에 저장된 SQL을 데이터 베이스에 전송한다.

이 과정에서 영속성 컨텍스트가 비워지는 것은 아니다. 

단지 영속성 컨텍스트의 변경 사항을 데이터베이스와 동기화를 할 뿐이다.

 

플러시를 하는 방법에는 3가지가 있다.

1. em.flush() 직접 호출

2. 트랜잭션 커밋 ( flush() 자동 호출)

3. JPQL 쿼리 실행 (flush() 자동 호출)

 

JPQL 쿼리 실행 시 flush가 자동 호출 되지 않게 설정을 할 수도 있다.

em.setFlushMode(FlushModeType.COMMIT) 커밋할 때만 플러시가 호출된다.

 

준영속 상태

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

준영속 상태로 만드는 방법

em.detach(entity) : 특정 엔티티만 준영속 상태로 만든다.

em.clear() : 영속성 컨텍스트를 초기화한다.

em.close() : 영속성 컨텍스트를 종료한다.

Member member = em.find(Member.class, 150L);
member.setName("AAAAA");

em.detach(member);

tx.commit();

위 코드처럼 member 엔티티를 detach 하면 commit()을 해도  setName에 대한 update SQL이 발생하지 않는다.

당연 DB에 이름이 변경되지도 않는다.

 

출처 : 자바 ORM 표 JPA 프로그래밍 - 기본편 김영한

'JPA' 카테고리의 다른 글

상속관계 매핑  (0) 2023.08.29
다양한 연관관계 매핑  (0) 2023.08.29
연관관계 매핑, 단방향, 양방향  (0) 2023.08.28
엔티티 매핑, 기본 키 매핑  (0) 2023.08.27
JPA 설정 하기  (0) 2023.08.26