✏️ Flush(플러시)

박상민·2023년 9월 27일
0

JPA

목록 보기
5/24
post-thumbnail

⭐️ Flush

Flush는 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영하는 것이다.
보통 데이터베이스 트랜잭션이 커밋될 때 플러시가 일어난다. 플러시는 복잡한 것이 아닌 이전에 소개한 쓰기 지연 SQL 저장소 쌓아둔 Insert, Update 등의 SQL이 데이터베이스에 날아가는 것이다.

쉽게 얘기해서 영속성 컨텍스트의 현재 변경 사양과 그 데이터베이스를 맞추는 작업이 플러쉬다.

데이터베이스 트랜잭션이 커밋되면 플러시가 자동으로 발생한다.

📌 Flush 발생

플러시가 발생하면 무슨 일이 생길까?

1.먼저 변경 감지(Dirty Checking)가 일어난다.

2.그러면서 수정된 엔티티를 쓰기 지연 SQL 저장소에 등록한다.

3.그리고 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송한다.

  • 쓰기 지연 SQL 저장소에는 등록 쿼리, 수정 쿼리, 삭제 쿼리 등이 다 담겨 있다.

4.저장소에 있는 쿼리들을 데이터베이스에 모두 보낸다.

플러시가 발생한다고 해서 데이터베이스 트랜잭션이 커밋되는 것은 아니다. 먼저 보내고 그 다음에 데이터베이스 트랜잭션을 커밋한다.

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

사실 영속성 컨텍스트를 직접 쓸 일을 거의 없다. 하지만 알아는 두는게 좋다.

  • 직접 호출
    • em.flush()
Member member = new Member("200L", "member200");
em.persist(member) //저장
em.flush(); // 강제 호출

System.out.println("=================="); //설명을 위한 구분선
tx.commit() //트랜잭션에 커밋

위와 같은 코드가 있다고 하자. 멤버를 생성하고 em.persist(member)로 저장을 했다. 그러나 트랜잭션에 커밋 되기 전까지는 이 쿼리를 볼 수가 없다. 그러나 미리 데이터베이스에 반영을 하고 싶거나 쿼리를 미리 보고싶다면 em.flush()를 해서 강제로 호출하면 된다. 그렇다면 이전에 소개한 플러시의 매커니즘이 즉시 일어난다.
즉, 데이터베이스의 Insert 쿼리가 em.flush를 하는 순간에 즉시 나가고 그 다음에 데이터베이스 트랜잭션이 커밋된다.


사진을 보면 구분선 전에 인서트 쿼리가 호출이 됐다. 이게 무슨 말이냐면 em.persist를 한 시점에는 영속성 컨텍스트에 멤버 엔티티가 담기고 쿼리가 저장소에 담겨있는 상태다. 그런데 이때 플러시를 호출하니까 쿼리가 DB에 바로 반영이 된 것이다.

혹시 플러시를 하게 되면 1차 캐시가 다 지워질까?
아니다. 1차 캐시는 그대로 다 유지가 되고 플러시라는 것은 1차 캐시를 지우지 않는다. 플러시는 기존 영속성 컨텍스트 속 쓰기 지연 SQL 저장소에 있는 쿼리들과 변경 감지가 일어나고 쓰기 지연 SQL에 쌓인 업데이트, 등록 쿼리 등이 데이터베이스에 반영되는 과정이라고 보면 된다.

  • 자동 호출
    • 트랜잭션 커밋 -> 플러시 호출
    • JPQL 쿼리 실행 -> 쿼리 실행 전 플러시 호출

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

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();

위처럼 member A,B,C를 저장했다고 하자. 이전에 말한 것처럼 이때까지 실제 데이터베이스에 쿼리가 안날라간다.

그리고 저장 후 바로 밑 코드에서 모든 멤버를 조회한다. 이때 조회가 될까?
될리가 없다. DB에 쿼리가 날라가야 DB에서 가져올텐데 지금 DB에 인서트하는 쿼리 자체가 안나갔다. 즉, DB에는 데이터가 없다.

그런데 JPQL은 ("select m from Member m", Member.class)이 그냥 SQL로 번역이 돼서 실행이 된다. 근데 인서트 쿼리가 안나가서 DB에서 가져올게 없다면 문제가 생긴다. 그래서 이런 문제를 방지하고자 JPQL은 기본으로 JPQL 쿼리(("select m from Member m", Member.class) 등)를 실행할 때는 무조건 플러시를 날려버린다. 그렇기 때문에 문제 없이 조회가 된다.

📌 Flush 모드 옵션

예시) em.setFlushMode(FlushModeType.COMMIT)

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

사실 우리가 Flush 모드 옵션을 쓸 일을 없다. 기본적으로 Auto로 되어 있는데 내가 트래잭션을 커밋하거나 쿼리를 실행할 때 플러시를 먼저 해주도록 하는 것이다.

FlushModeType.COMMIT?
이름 그대로 커밋을 할 때만 플러시가 일어는 모드 옵션이다. 쿼리를 실행할 때는 플러시를 안한다.
가끔 도움이 되는 경우가 있지만 그냥 AUTO로 쓰면 된다. 가급적 건들지 말고 쓰는 걸 권장한다.

📌 Flush는!

  • 러시라는 것은 영속성 컨텍스트를 비우는 게 아니다. 이름 때문에 오해하면 안되고 영속성 컨텍스트의 변경 내용을 DB에 동기화하는 게 플러시다.
  • 플러시라는 매커니즘이 동작할 수 있는 이유는 사실 트랜잭션이라는 작업 단위가 있기 때문이다.
  • 커밋 직전에만 동기화하면 된다. 트랜잭션 커밋 직전에만 변경 내용을 DB에 날려주면 되기 때문에 이런 매커니즘이 다 가능한 것이다.

JPA는 기본적으로 어떤 데이터를 맞추거나 이런 동시성에 대한 것은 다 데이터베이스 트랜잭션에 다 위임해서 사용한다.


출처
자바 ORM 표준 JPA 프로그래밍 강의
게시글에서 사용한 모든 사진 자료는 위 강의 속 자료입니다.

0개의 댓글

관련 채용 정보