본문으로 바로가기

[SPRING] JPA 플러시

category SPRING/기본 문법 2021. 3. 9. 16:17

플러시란?

플러시는 영속성 컨텍스트에 있는 등록, 수정, 삭제 처럼 모든 변경 내역을 데이터베이스에 반영하는 작업입니다. 결과적으로 변경 내역만 반영하고, 영속성 컨텍스트 자체는 플러시 후에도 유지됩니다.
플러시가 발생하면 쓰기 지연 SQL 저장소에 있는 내용을 DB에 반영하기 때문에 플러시가 끝나면 쓰기 지연 SQL 저장소의 내용을 비웁니다.

플러시가 발생하는 경우

• 변경 감지
• 수정된 엔티티 쓰기 지연 SQL 저장소에 등록
• 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송 (등록, 수정, 삭제 쿼리)

플러시가 자동으로 호출되는 경우

• em.flush() - 직접 호출

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

  • em.find는 영속성 컨텍스트에서 먼저 찾고 없으면 DB에서 찾습니다. em.find는 플러시가 자동으로 호출되지 않습니다.

플러시 모드 옵션

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

• FlushModeType.COMMIT : 커밋할 때만 플러시 (거의 사용 안함)

  • 사용하는 경우 : 앞에서 insert한 데이터와 상관없는 테이블을 조회하는 쿼리를 날리는 경우 플러시를 하는 의미가 없기 때문에 사용할 수 도 있으나 … 거의 사용하지 않는다.

플러시 주의사항

• 영속성 컨텍스트를 비우지 않음 (영속성 컨텍스트에 있는 1차캐시는 지워지지는 않는다.)
• 영속성 컨텍스트의 변경내용을 데이터베이스에 동기화
• 트랜잭션이라는 작업 단위가 중요 -> 커밋 직전에만 동기화 하면 됨

QnA

JPQL쿼리 실행 시 플러시가 자동으로 되는데 그렇다면 JPQL은 1차 캐시를 먼저 조회하지 않는 건가요?

JPQL은 1차 캐시를 먼저 조회하지 않습니다. JPQL을 실행하면 항상 1차 캐시를 무시하고,

데이터베이스에 직접 SQL을 실행합니다.

그리고 실행 결과를 1차 캐시에 보관하고, 최종적으로 1차 캐시에 보관된 결과를 반환합니다.

이런 방식으로 동작하는 이유는 em.find(식별자) 처럼 단순하게 식별자를 조회하는 경우는

1차 캐시에 있는지 없는지 판별하기가 쉬운데, JPQL은 광범위하게 데이터를 찾기 때문에 이런 방식의

구현이 어렵습니다. 그래서 우선 데이터베이스에서 조회부터 하는 것이지요. 추가로 JPQL을 실행해서,

데이터베이스에서 결과를 가져 왔는데, 이미 1차 캐시에 동일한 식별자를 가진 엔티티가 있으면,

데이터베이스에서 가져온 엔티티를 버리고 1차 캐시에 있는 엔티티를 유지합니다.

이런방식 덕분에 JPQL을 사용해도 엔티티 동일성을 유지합니다.

 


persist하고 중간에 JPQL로 조회 쿼리를 수행하면 이전 SQL이 flush가 되서 결과값이 나온다고 하셨는데, 실제로 DB에 COMMIT이 된 상태가 아니고, 단순히 INSERT 쿼리만 날린 상태인데 SELECT가 되는건가요?

DB는 같은 트랜잭션 범위에서 insert 한 다음에 commit하지 않아도 해당 데이터의 읽기가 가능합니다.

대신에 다른 트랜잭션에서는 앞서 설명한 insert 내용을 커밋 전까지 볼 수 없습니다.

 


1차캐시에 등록이 되어서 sql쿼리들을 쌓아놨던것들이 결국엔 flush를 만나면서 sql쿼리가 전송이 된다고 하셨는데 전송됨과 동시에 db가 업데이트 된건가요? flush를 만나면서 db까지 업데이트가 되는지 궁금합니다. 여기서 db가 업데이트 되지 않았으면 commit()이 호출될때 db가 업데이트 되는 것인가요? 아니면 db가 만약 업데이트 되었더라면 tx.commit을 적어주는게 의미있을까요?

플러시는 SQL을 데이터베이스에 전달하는 역할을 합니다. commit은 별도로 호출해주어야 합니다.

참고로 JPA의 commit을 호출하면 flush을 호출한 다음에 commit이 호출됩니다.