Back-End/JPA

[JPA] EntityManager의 flush와 @Transactional

유자맛바나나 2022. 4. 20. 03:43

 

 플러시(flush)

1. 플러시의 개념

  • 플러시는 영속성 컨텍스트의 변경 내용을 DB에 반영한다
  • 플러시라는 이름으로 인해 영속성 컨텍스트에 보관된 엔티티를 지운다고 생각하면 안된다. 영속성 컨텍스트의 변경 내용을 DB에 동기화하는 것이 플러시다.

[참고] Clear

  • 플러시를 하면 영속성 컨텍스트의 변경 내용을 DB에 반영(동기화)한다. 하지만 여전히 영속성 컨텍스트 내에는 기존에 보관된 엔티티 등의 정보가 캐시로 남아있다
  • Clear는 영속성 컨텍스트의 캐시를 전부 제거하는 역할을 해준다.

 

2. 플러시를 실행할 경우 구체적인 동작

  1. 영속성 컨텍스트에 있는 모든 엔티티를 스냅샷과 비교해 수정된 엔티티를 찾는다. 수정된 엔티티는 수정 쿼리를 만들어 쓰기 지연 SQL저장소에 등록한다
  2. 쓰기 지연 SQL 저장소의 쿼리를 DB에 전송한다(CREATE, UPDATE, DELETE 쿼리)

 

 플러시하는 세 가지 방법

1. 엔티티 매니저(EntityManger)의 flush()를 직접 호출

  • 엔티티 매니저의 flush() 메소드를 직접 호출해 영속성 컨텍스트를 강제 플러시할 수 있다
  • 테스트나 다른 프레임워크와 JPA를 함께 사용할 때를 제외하곤 거의 사용하지 않음

 

2. 트랜잭션 커밋 시 플러시 자동 호출

  • 변경 내용을 SQL로 DB에 전달하지 않고 트랜잭션만 커밋하면 DB에 변경사항이 반영되지 않는다
  • 따라서 트랜잭션 커밋 전 꼭 플러시를 통해 영속성 컨텍스트의 변경 내용이 DB에 반영되어야 한다.
  • JPA는 이런 문제를 예방하기 위해 트랜잭션을 커밋할 때 플러시를 자동으로 호출한다

 

3. JPQL 쿼리 실행 시 플러시 자동 호출

  • JPQL와 같은 객체지향 쿼리를 호출할 때도 플러시가 자동 실행된다
  • 아래 Example Code를 통해 그 이유에 대해 살펴본다

Example Code

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();
  • em.persist()를 통해 memberA, B, C는 영속 상태가 되었다. 하지만 해당 엔티티들은 영속성 컨텍스트에는 있지만 아직 DB에 반영된 상태는 아니다.
  • 이 상태에서 JPQL을 실행할 경우 SQL로 변환되어 DB에서 각 테이블을 조회하게 되는데, memberA, B, C가 영속성 컨텍스트에만 있다면 조회될 수 없다. 이러한 이유에서 JPA는 JPQL을 실행하기 전 플러시를 자동 호출해 변경 내용을 DB에 먼저 반영한다
  • 참고로, 식별자를 기준으로 조회하는 find() 메서드를 호출할 때는 플러시가 실행되지 않는다

 

[참고] 플러시 모드 옵션

  • 엔티티 매니저에 플러시 모드를 직접 지정하려면 javax.persistence.FlushModeType을 사용한다
    • FlushModeType.AUTO: 커밋이나 쿼리를 실행할 때 플러시(Default 값)
    • FlushModeType.COMMIT: 커밋할 때만 플러시
      예시) em.setFlushMode(FlushModeType.COMMIT)

 

 @Transactional

  • 스프링 프레임워크는 @Transactional 어노테이션이 붙어 있는 클래스나 메소드에 트랜잭션을 적용한다.
  • 외부에서 이 클래스의 메소드를 호출할 때 트랜잭션을 시작하고, 메소드를 종료할 때 트랜잭션을 커밋한다.
    (트랜잭션을 커밋한다는 것은 영속성 컨텍스트를 플러시 한다는 의미이기도 하다)
  • @Transactional(readOnly = true) 옵션을 적용할 경우 영속성 컨텍스트를 플러시하지 않는다
    (*readOnly = false가 디폴트)
  • 만약 예외가 발생하면 트랜잭션을 롤백한다. 참고로, @Transactional은 RuntimeException과 그 자식들인 언체크(Unchecked) 예외만 롤백한다. 만약 체크(Checked) 예외가 발생해도 롤백하고 싶다면 @Transactional(rollbackFor = Exception.class)와 같이 예외를 지정해야 한다.

 

 

❑ Reference

자바 ORM 표준 JPA 프로그래밍 | 김영한 | 에이콘 출판 

'Back-End > JPA' 카테고리의 다른 글

[Spring] JPA를 사용한 MySQL 연동(생성/조회) 예제  (0) 2021.07.19