y1551463

JPA - 영속성 컨텍스트 (flush와 commit의 차이)

JPA영속성컨텍스트TIL
a month ago
·
8 min read

영속성 컨텍스트 - 동작방식

  • 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 저장소에 저장

변경 감지의 원리

  1. 영속 상태로 관리되는 객체의 초기 상태를 스냅샷으로 저장한다

    • persist() 호출 시 비영속 객체를 영속성 컨텍스트에 등록하며 이때 초기 상태를 내부적으로 저장한다 (스냅샷)

  2. 트랜잭션 내에서 객체가 변경되었는지 확인한다

    • 트랜잭션 종료 전 flush() 시점에 스냅샷과 현재 영속 객체의 상태를 비교한다

  3. 변경 사항이 있다면 적절한 SQL을 생성한다

    • 변경된 필드에 대해 수정 또는 삭제 쿼리를 생성해서 SQL 저장소에 추가한다

의문점에 대한 정리

  • Q. 실행한 자바 프로그램에서는 반영이 되었지만 H2 콘솔프로그램이 바라보는 DB에서는 반영이 되지 않았다, 왜 실제 DB에는 반영이 안 되었는지

    • A. 둘은 서로 다른 트랜잭션을 가지고 있기 때문.

      • 트랜잭션의 원칙 중 격리성에 해당되는 것으로 이해했는데 서로 다른 트랜잭션이기 때문에 자바 프로그램의 트랜잭션이 commit (확정)되지 않았기 때문에 DB 트랜잭션에서는 볼 수 없음

  • Q. commit을 해야만 실제 DB에 반영이 되는데 commit을 하게 되면 flush도 실행된다고 해서 그럼 flush는 굳이 왜 사용하는지가 이해되지 않았다

    • A. 둘의 역할이 분명하게 다르다

      • flush는 변경 사항을 데이터베이스로 전송하므로써 데이터베이스의 트랜잭션 버퍼에 저장만 해두고 (기록)

        • flush를 명시적으로 호출해도 무방하다고 하지만 권장하지는 않는다는다는 것 같다

        • JPQL 같은 경우 해당 트랜잭션 내에서는 DB와 일관성을 가져야 수행이 되는 작업이라 자동으로 flush가 선행되는 것 같다 -> 이것은 아직 추측..

      • commit은 이를 확정할지 rollback 처리하여 취소시킬지를 결정한다


- 컬렉션 아티클