Flush는 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영하는 것이다.
보통 데이터베이스 트랜잭션이 커밋될 때 플러시가 일어난다. 플러시는 복잡한 것이 아닌 이전에 소개한 쓰기 지연 SQL 저장소
쌓아둔 Insert, Update 등의 SQL이 데이터베이스에 날아가는 것이다.
쉽게 얘기해서 영속성 컨텍스트의 현재 변경 사양과 그 데이터베이스를 맞추는 작업이 플러쉬다.
데이터베이스 트랜잭션이 커밋되면 플러시가 자동으로 발생한다.
플러시가 발생하면 무슨 일이 생길까?
1.먼저 변경 감지(Dirty Checking)가 일어난다.
2.그러면서 수정된 엔티티를 쓰기 지연 SQL 저장소에 등록한다.
3.그리고 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송한다.
4.저장소에 있는 쿼리들을 데이터베이스에 모두 보낸다.
플러시가 발생한다고 해서 데이터베이스 트랜잭션이 커밋되는 것은 아니다. 먼저 보내고 그 다음에 데이터베이스 트랜잭션을 커밋한다.
사실 영속성 컨텍스트를 직접 쓸 일을 거의 없다. 하지만 알아는 두는게 좋다.
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에 쌓인 업데이트, 등록 쿼리 등이 데이터베이스에 반영되는 과정이라고 보면 된다.
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)
등)를 실행할 때는 무조건 플러시를 날려버린다. 그렇기 때문에 문제 없이 조회가 된다.
예시) em.setFlushMode(FlushModeType.COMMIT)
사실 우리가 Flush 모드 옵션을 쓸 일을 없다. 기본적으로 Auto로 되어 있는데 내가 트래잭션을 커밋하거나 쿼리를 실행할 때 플러시를 먼저 해주도록 하는 것이다.
FlushModeType.COMMIT?
이름 그대로 커밋을 할 때만 플러시가 일어는 모드 옵션이다. 쿼리를 실행할 때는 플러시를 안한다.
가끔 도움이 되는 경우가 있지만 그냥 AUTO로 쓰면 된다. 가급적 건들지 말고 쓰는 걸 권장한다.
JPA는 기본적으로 어떤 데이터를 맞추거나 이런 동시성에 대한 것은 다 데이터베이스 트랜잭션에 다 위임해서 사용한다.
출처
자바 ORM 표준 JPA 프로그래밍 강의
게시글에서 사용한 모든 사진 자료는 위 강의 속 자료입니다.