JPA/JPA

[JPA] 플러시와 준영속 상태

yoon_seon 2023. 4. 11. 18:55

플러시(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