[JPA] 플러시와 준영속 상태
플러시(flush)
- 영속성 컨텍스트의 변경내용을 데이터베이스에 반영하는 것.
- 플러시 발생
- 변경 감지가 발생하면 수정된 엔티티를 쓰기 지연 SQL 저장소에 등록한다.
- 트랜잭션 커밋시점에 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송한다.(INSERT, UPDATE, DELETE)
영속성 컨텍스트를 플러시 하는 방법
- em.flush() : 직접 호출
- 트랜잭션 커밋 : 플러시 자동 호출
- JPQL 쿼리 실행 : 플러시 자동 호출
em.flush()
- 미리 DB에 저장하고 싶거나 미리 SQL을 보고 싶을 경우 em.flush()를 호출한다.
- 호출 즉시 플러시가 발생한다.
public class Flush {
public static void main(String args[]) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Member member = new Member(3L, "member200");
em.persist(member);
em.flush();
System.out.println("=======================");
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
로그를 보면 트랜잭션 커밋 시점이 아닌 플러시를 한 즉시 SQL이 반영되고 후 하이픈이 찍힌 것을 확인할 수 있다.
JPQL 쿼리 실행시 플러시가 호출되는 이유
트랜잭션 커밋 전 영속성 컨테이너에는 올라갔지만 DB에 반영되지 않은 데이터를 JPQL을 실행했을 때 영속성 컨테이너에 있는 데이터를 SQL로 가져와야 하는 경우가 있기 때문에 JPQL쿼리 실행 시 플러시가 호출된다.
플러시 모드 옵션
- FlushModeType.AUTO : 커밋이나 SQL을 실행할 때 플러시 (기본값)
- FlushModeType.COMMIT : 커밋할 때만 플러시
정리
- 플러시가 발생해도 영속성 컨텍스트를 비우지 않는다. 단순히 쓰기 지연 SQL 저장소에 있는 SQL이 반영만 된다.
- 영속성 컨텍스트의 변경내용을 데이터베이스에 동기화
- 트랜잭션이라는 작업 단위가 중요 → 커밋 직전에만 동기화하면 된다.
준영속 상태
- 영속으로 준영속상태로 변경.
- 영속 상태의 엔티티가 영속성 컨텍스트에서 분리된 상태(detached)
- 영속성 컨텍스트가 제공하는 기능을 사용하지 못한다.
준영속 상태로 만드는 방법
- em.detach(entity) : 특정 엔티티만 준영속 상태로 전환
- em.clear() : 영속성 컨텍스트를 완전히 초기화
- em.close() : 영속성 컨텍스트를 종료
em.detach(entity)
public class Detach {
public static void main(String args[]) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Member member = em.find(Member.class, 3L);
member.setName("이름변경");
em.detach(member);
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
트랜잭션 커밋을 했지만 SELECT SQL만 출력되고 UPDATE SQL은 출력되지 않았다. em.datach(member); 로 member 엔티티를 영속상태에서 준영속상태로 전환했기 때문이다.
em.clear()
public class Clear {
public static void main(String args[]) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Member member1 = em.find(Member.class, 3L);
member1.setName("이름변경");
em.clear();
Member member2 = em.find(Member.class, 3L);
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
첫 번째 엔티티 조회 시 DB에서 데이터를 조회한다.
두 번째 엔티티 조회 시 em.clear()으로 영속성 컨테이너를 초기화하여 1차 캐시가 모두 비워졌기 때문에 DB에서 조회한다.
SQL도 SELECT SQL이 두번 출력된 것을 확인할 수 있다.
해당 글은 인프런의 [자바 ORM 표준 JPA 프로그래밍 - 기본편] 강의를 정리한 내용입니다.
자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의
JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., - 강의 소개 | 인프런
www.inflearn.com