본문 바로가기
Study/spring

자바 ORM 표준 JPA 프로그래밍(4) - 플러시와 준영속

by 유경호 2020. 12. 23.
반응형

플러시(flush())

  • 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영한다.
  • 변경 감지를 통해 영속성 컨텍스트에 있는 엔티티들과 스냅샷을 비교하여 변경된 부분을 반영한다.
  • 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 날린다.

 

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

  • em.flush(): 직접 호출
  • 트랜잭션 커밋: 트랜잭션 커밋시에 플러시 실행
  • JPQL 쿼리 실행: JPQL이나 Criteria 같은 객체지향 쿼리가 호출될 때 플러시 실행

 

JPQL 쿼리 실행 시 플러시가 자동으로 호출되는 이유

1
2
3
4
5
6
em.persist(memberA);
em.persist(memberB);
em.persist(memberC);
//중간에 JPQL 실행
query = em.createQuery("select m from Member m", Member.class);
List<Member> members= query.getResultList();
cs

→ 기본적으로 persist로 의해 적재된 엔티티들은 쓰기 지연 SQL 저장소에 적재되어 있다가 커밋시에 데이터베이스에 반영된다 하지만 그 전에 JPQL로 조회하면 memberA, memberB, memberC에 대한 조회 결과를 받을 수 없으므로 이를 예방하기 위해 먼저 플러시를 실행하고 JPQL이 실행된다.

 

플러시 모드 옵션

em.setFlushMode(FlushModeType)

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

 

플러시 정리

  • 영속성 컨텍스트를 비우지 않음
  • 영속성 컨텍스트의 변경내용을 데이터베이스에 동기화
  • 트랜잭션이라는 작업 단위가 있기에 가능함 → 커밋 직전에만 동기화
    하면 됨

준영속 상태

  • 영속 상태에서 준영속로 변환 가능
  • 영속 상태의 엔티티가 영속성 컨텍스트에서 분리(detached)
  • 영속성 컨텍스트가 제공하는 기능을 사용 못함

 

준영속 상태로 만드는 방법

  • em.detach(entity): 특정 엔티티만 준영속 상태로 전환
  • em.clear(): 영속성 컨텍스트를 완전히 초기화
  • em.close(): 영속성 컨텍스트를 종료

 

엔티티를 준영속 상태로 전환: detach()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void deteachedTest() {
    EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
    EntityManager em = emf.createEntityManager();
    EntityTransaction tx = em.getTransaction();
    tx.begin();
 
    // 회원 엔티티 생성, 비영속(new/transient) 상태
    Member member = new Member();
    member.setName("Kyeongho");
 
    // 회원 엔티티 영속(managed) 상태
    em.persist(member);
 
    // 회원 엔티티를 영속성 컨텍스트에서 분리, 준영속(detached) 상태
    em.detach(member);
 
    tx.commit();
    em.close();
 }
cs

→ em.detach(memeber)를 호출한 시점에서 1차 캐시부터 쓰기 지연 SQL 저장소까지 해당 엔티티를 관리하기 위한 모든 정보가 제거된다. 이렇게 영속 상태였다가 영속성 컨텍스트가 관리하지 않는 상태가된 상태를 준영속 상태라 한다.

 

영속성 컨텍스트 초기화: clear()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public void ClearTest() {
    EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
    EntityManager em = emf.createEntityManager();
    EntityTransaction tx = em.getTransaction();
 
    tx.begin();
 
    Member member1 = new Member();
    member1.setName("Kyeongho");
 
    // 새 엔티티 persist()이후 flush()를 통해 디비에 저장
    em.persist(member1);
    em.flush();
 
    // 엔티티 조회, 영속 상태
    Member member2 = em.find(Member.class, 1L);
 
    em.clear(); // 영속성 컨텍스트 초기화
 
    // 준영속 상태
    member2.setName("ModifiedName");
 
    tx.commit();
 
    em.close();
}
cs

→ member2에서 조회된 엔티티는 영속 상태이다. 그런 다음 clear()를 호출하게 되면 영속성 컨텍스트에 있는 모든 것이 초기화되기 때문에 member2도 준영속 상태가 된다.

 

병합: merge()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public void mergeTest() {
    EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
    EntityManager em = emf.createEntityManager();
    EntityTransaction tx = em.getTransaction();
    tx.begin();
    // 비영속 상태
    Member member1 = new Member();
    member1.setName("Kyeongho");
 
    // 영속 상태
    em.persist(member1);
 
    // 준영속 상태
    em.detach(member1);
 
        // 병합, 영속 상태
    Member mergeMember = em.merge(member1);
 
    System.out.println("em.contains(member1) = " + em.contains(member1));
    System.out.println("em.contains(mergeMember) = " + em.contains(mergeMember));
    tx.commit();
 
    em.close();
}
cs

실행결과

Hibernate: 
    /* insert me.kyeongho.Member
        */ insert 
        into
            Member
            (id, name) 
        values
            (null, ?)
Hibernate: 
    /* load me.kyeongho.Member */ select
        member0_.id as id1_0_0_,
        member0_.name as name2_0_0_ 
    from
        Member member0_ 
    where
        member0_.id=?
em.contains(member1) = false
em.contains(mergeMember) = true

→ merge() 호출 시 준영속 상태의 엔티티를 통해 새롭게 병합된 영속 상태의 엔티티로 반환한다. 기존의 준영속 상태의 엔티티는 계속 준영속 엔티티로 남아있다.

 

비영속 병합

1
2
3
Member member = new Member();
Member newMember = em.merge(member);
tx.commit();
cs

→병합(merge())는 비영속 엔티티도 영속 상태로 만들 수 있다. 즉 병합은 비영속, 준영속을 신경 쓰지 않는다.

반응형