JPA - 영속성 컨텍스트 (flush와 commit의 차이)
영속성 컨텍스트 - 동작방식
EntityManager.persist() 를 통해 영속 객체를 컨텍스트에 등록하면 우선적으로 해당 영속 객체는 1차 캐시에 저장되고 트랜잭션이 commit 되는 시점에 insert 쿼리가 데이터베이스에 반영된다
EntityManager.find() 를 통해 데이터를 조회하면 우선적으로 1차 캐시 내에서 해당 영속 객체를 찾고 없다면 select 쿼리를 통해 데이터베이스에서 조회한다
영속성 컨텍스트 - 변화 감지
영속성 컨텍스트를 통해 관리되는 영속 객체는 객체의 상태 변경에 따라 update 쿼리가 생성되고 (sql 저장소) commit 시점에 데이터베이스에 반영된다
영속 객체의 변경 사항은 1차 캐시의 스냅샷을 통해 판별한다
영속 객체의 상태가 변경되면 1차 캐시의 스냅샷과 비고 -> 다르다면 update 쿼리를 생성해 sql 저장소에 저장 -> 반영
영속성 컨텍스트 - flush
flush는 영속성 컨텍스트의 내용을 데이터베이스와 동기화 하는 것을 의미한다
3가지 방식
EntityManager.flush() 를 직접 호출
트랜잭션의 commit을 통해 자동으로
JPQL 쿼리 실행을 통해 자동으로
flush를 실행한 후에도 영속성 컨텍스트의 내용은 그대로 유지된다
flush에 대해서는 조금 더 이해가 필요한 것 같음
flush와 commit의 차이 (+ 11/19)
flush가 데이터베이스와 동기화 하는 것을 의미하는데 flush를 통해 동기화를 하지만 commit을 하지 않으면 실제 DB에는 반영이 되지 않는 것을 확인했다
실행한 자바 프로그램에서는 반영이 되었지만 H2 콘솔프로그램이 바라보는 DB에서는 반영이 되지 않았다
의아했던 부분은 왜 실제 DB에는 반영이 안 되었는지와 commit을 해야만 실제 DB에 반영이 되는데 commit을 하게 되면 flush도 실행된다고 해서 그럼 flush는 굳이 왜 사용하는지가 이해되지 않았다
flush
영속성 컨텍스트의 변경 내용을 데이터베이스에 반영하는 작업 (동기화)
SQL 쿼리는 DB로 전송되지만 해당 트랜잭션이 commit 되지 않으면 최종 반영은 되지 않는다
즉 변경된 사항은 DB 트랜잭션에 머물러있는 상태이지만 영구적인 저장 (영속화)은 되지 않는 상태
flush가 필요한 이유는 영속성 컨텍스트의 쓰기 지연 메커니즘 때문에 필요
JPA는 변경사항에 대해 실시간으로 즉시 DB에 반영하지 않고 최적화된 SQL을 한번에 전송하기 위해서 쓰기 지연을 사용한다
트랜잭션 안에서 발생되는 SQL 쿼리를 쓰기 지연 SQL 저장소에 저장하고 한번에 전송한다고 이해했음
변경 감지(Dirty Checking)를 수행
flush가 실행될 때 SQL 저장소에 있는 쿼리를 데이터베이스로 전송된다
전송된 SQL은 데이터베이스 트랜잭션 버퍼라는 곳에 기록은 되지만 commit 전까지는 확정이 아니다
commit
해당 트랜잭션을 종료하고 변경 사항을 DB에 영구 반영한다
데이터베이스 트랜잭션을 확정한다
즉, flush는 SQL을 데이터베이스로 보내는 작업이고, commit은 데이터베이스에 변경 내용을 최종 반영하는 작업
변경 감지(Dirty Checking)
flush 시점에 자동으로 수행
영속성 컨텍스트 1차 캐시에 있는 영속 상태의 객체와 데이터베이스 내 초기 상태를 비교하여 변경 사항이 있다면 적절한 SQL을 생성하여 SQL 저장소에 저장
변경 감지의 원리
영속 상태로 관리되는 객체의 초기 상태를 스냅샷으로 저장한다
persist() 호출 시 비영속 객체를 영속성 컨텍스트에 등록하며 이때 초기 상태를 내부적으로 저장한다 (스냅샷)
트랜잭션 내에서 객체가 변경되었는지 확인한다
트랜잭션 종료 전 flush() 시점에 스냅샷과 현재 영속 객체의 상태를 비교한다
변경 사항이 있다면 적절한 SQL을 생성한다
변경된 필드에 대해 수정 또는 삭제 쿼리를 생성해서 SQL 저장소에 추가한다
의문점에 대한 정리
Q. 실행한 자바 프로그램에서는 반영이 되었지만 H2 콘솔프로그램이 바라보는 DB에서는 반영이 되지 않았다, 왜 실제 DB에는 반영이 안 되었는지
A. 둘은 서로 다른 트랜잭션을 가지고 있기 때문.
트랜잭션의 원칙 중 격리성에 해당되는 것으로 이해했는데 서로 다른 트랜잭션이기 때문에 자바 프로그램의 트랜잭션이 commit (확정)되지 않았기 때문에 DB 트랜잭션에서는 볼 수 없음
Q. commit을 해야만 실제 DB에 반영이 되는데 commit을 하게 되면 flush도 실행된다고 해서 그럼 flush는 굳이 왜 사용하는지가 이해되지 않았다
A. 둘의 역할이 분명하게 다르다
flush는 변경 사항을 데이터베이스로 전송하므로써 데이터베이스의 트랜잭션 버퍼에 저장만 해두고 (기록)
flush를 명시적으로 호출해도 무방하다고 하지만 권장하지는 않는다는다는 것 같다
JPQL 같은 경우 해당 트랜잭션 내에서는 DB와 일관성을 가져야 수행이 되는 작업이라 자동으로 flush가 선행되는 것 같다 -> 이것은 아직 추측..
commit은 이를 확정할지 rollback 처리하여 취소시킬지를 결정한다