코딩하는 털보

21.10.05 TIL 본문

Diary/Today I Learned

21.10.05 TIL

이정인 2021. 10. 9. 20:16

JPA

Dirty Checking

영속 엔티티(managed)가 수정되면 JPA가 Dirty Checking 기능으로 엔티티의 변경을 감지하여 DB와 1차 캐시의 일관성을 맞춘다.

Dirty Checking : 1차 캐시의 스냅샷(1차 캐시에 캐싱될 때의 엔티티 상태)과 현재 엔티티의 상태를 비교하는 것, 변경이 있으면 update SQL을 생성하여 쓰기 지연 SQL 저장소에 저장한다.

Flush

영속성 컨텍스트의 변경 내용을 데이터베이스에 반영하는 것.

플러시가 발생하면 어떤일이 일어나는가?

  • Dirty Checking
  • 감지된 Update 쿼리를 쓰기 지연 SQL 저장소에 저장
  • 쓰기 지연 SQL 저장소의 SQL 쿼리들을 DB로 전송

그럼 플러시는 언제 발생하는가?

  • em.flush()
  • 트랜잭션 커밋
  • JPQL 쿼리 실행
    find()는 1차 캐시에서 찾으면 되지만, JPQL은 실제 SQL이 발생하게 되므로…

플러시 모드 옵션 (em.setFlushMode())

  • FlushModeType.Auto : 커밋 또는 쿼리 실행 시 플러시 (default)
  • FlushModeType.Commit : 커밋할 때만 플러시

Flush 테스트 해보기

플러시가 발생하는 조건인 세가지 동작에서 정말로 플러시가 잘 발생하는지 테스트 했다.
("hello" persistence-unit은 persistence.xml에 정의했다.)
persist, remove, update 모두 쓰기지연을 통해 플러시 할 때까지 SQL을 모아서 DB에 전달한다.
FlushModeType.Auto모드(기본)에서는 아래 처럼 JPQL을 사용하면 조회만 하는데도 플러시를 발생시켜서 update 쿼리가 발생한다.

@SpringBootApplication
public class JpaApplication {

    public static void main(String[] args) {
        SpringApplication.run(JpaApplication.class, args);
    }

    @PostConstruct
    void started() {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        EntityTransaction transaction = em.getTransaction();
        transaction.begin();
        try {
            User user = new User("jilee", "demo", "tuna");

            //엔티티 비영속 -> 영속
            em.persist(user);
            user.setNickname("rockintuna");

            // flush //insert, update
            em.flush();

            user.setNickname("rockintuna2");

            // flush //update, select
            User pickedUser = em.createQuery("select u from User as u", User.class).getSingleResult();

            user.setNickname("rockintuna3");
            em.remove(user);

            // flush //delete
            transaction.commit();
        } catch (Exception e) {
            transaction.rollback();
        } finally {
            em.close();
            emf.close();
        }
    }
}

FlushModeType.Commit 모드에서는 flush() 메서드나 commit은 Auto 모드와 동일하게 동작했고
다른 점은 JPQL에서 플러시가 발생하지 않았기 때문에 select문만 DB에 요청했다.

@SpringBootApplication
public class JpaApplication {

    public static void main(String[] args) {
        SpringApplication.run(JpaApplication.class, args);
    }

    @PostConstruct
    void started() {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        em.setFlushMode(FlushModeType.COMMIT);
        EntityTransaction transaction = em.getTransaction();
        transaction.begin();
        try {
            User user = new User("jilee", "demo", "tuna");

            //엔티티 비영속 -> 영속
            em.persist(user);

            //영속 엔티티 변경
            user.setNickname("rockintuna");
            // flush //insert, update
            em.flush();

            user.setNickname("rockintuna2");
            // flush // select
            User pickedUser = em.createQuery("select u from User as u", User.class).getSingleResult();

            user.setNickname("rockintuna3");
            em.remove(user);

            // flush //delete
            transaction.commit();
        } catch (Exception e) {
            transaction.rollback();
        } finally {
            em.close();
            emf.close();
        }
    }
}
Comments