[기술면접 대비] JPA의 더티 체킹이란 무엇인가요?

김엄지·2024년 4월 9일
0

기술면접

목록 보기
4/4

핵심 키워드 : 변경 감지, 커밋, 스냅샷, Transaction

  • JPA의 더티 체킹은 JPA의 성능 최적화 기능 중 하나로서, 엔티티의 상태를 영속성 컨텍스트에서 추적하여 변경 여부를 자동으로 감지하는 메커니즘입니다.
    • 엔티티가 로딩될 때 이전 상태의 스냅샷이 저장되며, 트랜잭션 커밋 시 현재 상태와 비교하여 변경된 필드를 감지합니다.
    • 변경된 필드만을 이용해 효율적인 UPDATE 쿼리를 생성하고 데이터베이스에 반영하며,
      이는 개발자가 업데이트 쿼리를 작성하지 않아도 변경 사항을 효과적으로 반영할 수 있게 도와줍니다.

✅ JPA에서 더티 체킹

효율적인 데이터 업데이트 작업을 가능하게 하는 중요한 기능입니다. 이 과정은 다음과 같이 이루어집니다.

  • 변경 감지(Dirty Checking) Entity가 영속성 컨텍스트에 로드될 때의 초기 상태(스냅샷)을 저장합니다. Transaction이 커밋되기 전에, 영속성 컨텍스트는 Entity의 현재 상태와 시냅샷을 비교하여 변경 사항을 감지합니다.
  • 변경 사항 자동 반영 변경된 필드가 있는 경우, JPA는 해당 Entity에 대한 UPDATE SQL을 자동으로 생성하고 실행하여 데이터베이스를 업데이트합니다.

이 기능 덕분에 개발자는 데이터를 업데이트하기 위해 별도의 update 메서드를 호출할 필요가 없습니다. Entity 객체의 값을 변경하기만 하면 JPA가 나머지를 알아서 처리합니다. 이는 애플리케이션 코드를 더 간결하고 명확하게 만들어 줍니다.

✅ 영속성 컨텍스트(Persistence Context)란

  • JPA에서 Entity 객체를 영구 저장하는 환경을 의미합니다.
  • Persistence는 영속성, 지속성이라는 뜻입니다. 객체 관점으로 보면, 객체가 유지되는 시간, 객체의 위치를 자유롭게 유지하고 이동할 수 있는 객체의 성질을 의미합니다.
  • 직접 SQL을 작성하지 않아도 JPA를 사용하여 DB에 데이터를 저장, 조회, 수정, 삭제도 가능합니다.
  • 이러한 일련의 과정을 효율적으로 처리하기 위해 JPA는 영속성 컨텍스트에 Entity 객체들을 저장하여 관리하며 DB와 소통합니다. Untitled
  • EntityManader와 EntityManagerFactory 영속성 컨텍스트에 접근하여 Entity 객체들을 조작하기 위해서는 EntityManagerFactory를 통해 생성한 EntityManager가 필요합니다.
    • EntityManagerFactory는 일반적으로 DB 하나에 하나만 생성되어 애플리케이션이 동작하는 동안 사용됩니다.
    • EntityManagerFactory를 만들기 위해서는 DB에 대한 정보를 전달해야합니다.
      • 정보를 전달하기 위해서는 /resources/META-INF/ 위치에 persistence.xml 파일을 만들어 정보를 넣어두면 됩니다.
        # 생성한 database name : memo
        <?xml version="1.0" encoding="UTF-8"?>
        <persistence version="2.2"
                     xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                     xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
            <persistence-unit name="memo">
                <class>com.sparta.entity.Memo</class>
                <properties>
                    <property name="jakarta.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
                    <property name="jakarta.persistence.jdbc.user" value="root"/>
                    <property name="jakarta.persistence.jdbc.password" value="{비밀번호}"/>
                    <property name="jakarta.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/memo"/>
        
                    <property name="hibernate.hbm2ddl.auto" value="create" />
        
                    <property name="hibernate.show_sql" value="true"/>
                    <property name="hibernate.format_sql" value="true"/>
                    <property name="hibernate.use_sql_comments" value="true"/>
                </properties>
            </persistence-unit>
        </persistence>
        # JPA는 **persistence.xml** 의 정보를 토대로 EntityManagerFactory를 생성
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("memo");
        
        # EntityManagerFactory를 사용하여 EntityManager를 생성
        EntityManager em = emf.createEntityManager();
  • JPA의 Transaction 트랜잭션은 DB 데이터들의 무결성과 정합성을 유지하기 위해 생겨난 개념입니다. Untitled
    • 여러 개의 SQL이 하나의 트랜잭션에 포함될 수 있습니다.

    • 모든 SQL이 성공적으로 수행이 되면 DB에 영구적으로 변경을 반영하지만, SQL 중 단 하나라도 실패한다면 모든 변경을 되돌립니다.

      Untitled

    • JPA에서도 영속성 컨텍스트로 관리하고 있는 변경이 발생한 객체들의 정보를 쓰기 지연 저장소에 전부 가지고 있다가 마지막에 SQL을 한번에 DB에 요청해 변경을 반영합니다.

    • EntityTransaction 성공 테스트

      @Test
      @DisplayName("EntityTransaction 성공 테스트")
      void test1() {
          EntityTransaction et = em.getTransaction(); // EntityManager 에서 EntityTransaction 을 가져옵니다.
      
          et.begin(); // 트랜잭션을 시작합니다.
      
          try { // DB 작업을 수행합니다.
      
              Memo memo = new Memo(); // 저장할 Entity 객체를 생성합니다.
              memo.setId(1L); // 식별자 값을 넣어줍니다.
              memo.setUsername("Robbie");
              memo.setContents("영속성 컨텍스트와 트랜잭션 이해하기");
      
              em.persist(memo); // EntityManager 사용하여 memo 객체를 영속성 컨텍스트에 저장합니다.
      
              et.commit(); // 오류가 발생하지 않고 정상적으로 수행되었다면 commit 을 호출합니다.
              // commit 이 호출되면서 DB 에 수행한 DB 작업들이 반영됩니다.
          } catch (Exception ex) {
              ex.printStackTrace();
              et.rollback(); // DB 작업 중 오류 발생 시 rollback 을 호출합니다.
          } finally {
              em.close(); // 사용한 EntityManager 를 종료합니다.
          }
      
          emf.close(); // 사용한 EntityManagerFactory 를 종료합니다.
      }

✅ 영속성 컨텍스트의 기능

  • 1차 캐시
    • 영속성 컨텍스트는 내부적으로 캐시 저장소를 가지고 있습니다. 우리가 저장하는 Entity 객체들이 1차 캐시(즉, 캐시 저장소)에 저장됩니다.
    • 캐시 저장소는 Map 자료구조 형태로 되어있습니다.
      • key에는 @Id로 매핑한 기본 키(즉, 식별자 값)를 저장합니다.
      • value에는 해당 Entity 클래스의 객체를 저장합니다.
      • 영속성 컨텍스트는 캐시 저장소 Key에 저장한 식별자값을 사용하여 Entity 객체를 구분하고 관리합니다. Untitled
    • 1차 캐시 사용의 장점
      • DB 조회 횟수를 줄일 수 있습니다.
      • 1차 캐시를 사용해 DB row 1개 당 객체 1개가 사용되는 것을 보장합니다(객체 동일성 보장).
    • Entity 저장
      • em.persist(memo); 메서드가 호출되면 memo Entity 객체를 캐시 저장소에 저장합니다. Untitled
        • 예시 코드로 확인하기
          @Test
          @DisplayName("1차 캐시 : Entity 저장")
          void test1() {
              EntityTransaction et = em.getTransaction();
          
              et.begin();
          
              try {
          
                  Memo memo = new Memo();
                  memo.setId(1L);
                  memo.setUsername("Robbie");
                  memo.setContents("1차 캐시 Entity 저장");
          
                  em.persist(memo); // 캐시 저장소에 저장
          
                  et.commit();
          
              } catch (Exception ex) {
                  ex.printStackTrace();
                  et.rollback();
              } finally {
                  em.close();
              }
          
              emf.close();
          }
    • Entity 조회
      • 1, 캐시 저장소에 조회하는 Id가 존재하지 않은 경우
        • 캐시 저장소 조회 Untitled
        • DB SELECT 조회 후 캐시 저장소에 저장 Untitled
          • em.find(Memo.class, 1); 호출 시 캐시 저장소를 확인 한 후 해당 값이 없다면?
          • DB에 SELECT 조회 후 해당 값을 캐시 저장소에 저장하고 반환합니다.
        • 예시 코드로 확인하기
          @Test
          @DisplayName("Entity 조회 : 캐시 저장소에 해당하는 Id가 존재하지 않은 경우")
          void test2() {
              try {
          
                  Memo memo = em.find(Memo.class, 1);
                  System.out.println("memo.getId() = " + memo.getId());
                  System.out.println("memo.getUsername() = " + memo.getUsername());
                  System.out.println("memo.getContents() = " + memo.getContents());
          
              } catch (Exception ex) {
                  ex.printStackTrace();
              } finally {
                  em.close();
              }
          
              emf.close();
          }
          • DB에서 데이터를 조회만 하는 경우에는 데이터의 변경이 발생하는 것이 아니기 때문에 트랜잭션이 없어도 조회가 가능합니다.
          • Memo memo = em.find(Memo.class, 1); 호출 시 캐시 저장소에 해당 값이 존재하지 않기 때문에 DB에 SELECT 조회하여 캐시 저장소에 저장한 후 반환합니다.
        1. 캐시 저장소에 조회하는 Id가 존재하는 경우
        • em.find(Memo.class, 1); 호출 시 캐시 저장소에 식별자 값이 1이면서 Memo Entity 타입인 값이 있는지 조회합니다.
        • 값이 있다면 DB에 조회하지 않고 캐시 저장소에서 해당 Entity 객체를 반환합니다. Untitled
    • Entity 삭제
      • 삭제할 Entity를 조회한 후, 캐시 저장소에 없다면 DB에 조회해서 저장합니다. Untitled
      • em.remove(entity); Untitled
        • em.remove(memo); 호출 시 삭제할 Entity를 DELETED 상태로 만든 후
        • 트랜잭션 commit 후 Delete SQL이 DB에 요청됩니다.
      • 예시 코드로 확인하기
        @Test
        @DisplayName("Entity 삭제")
        void test5() {
            EntityTransaction et = em.getTransaction();
        
            et.begin();
        
            try {
        
                Memo memo = em.find(Memo.class, 2);
        
                em.remove(memo);
        
                et.commit();
        
            } catch (Exception ex) {
                ex.printStackTrace();
                et.rollback();
            } finally {
                em.close();
            }
        
            emf.close();
        }
  • 쓰기 지연 저장소(ActionQueue)
    • JPA의 트랜잭션을 학습하면서 JPA가 트랜잭션 처럼 SQL을 모아서 한번에 DB에 반영한다는 것을 배웠습니다.
      • JPA는 이를 구현하기 위해 쓰기 지연 저장소를 만들어 SQL을 모아두고 있다가 트랜잭션 commit 후 한번에 DB에 반영합니다.
  • 변경 감지(Dirty Checking)

✅ Entity의 상태

Untitled

  • 비영속(Transient)
  • 영속(Managed)
  • 준영속(Detached)
  • 삭제(Removed)

✅ SpringBoot의 JPA

✅ Spring Data JPA란 무엇일까

마무리

이번 내용은 다시 공부해보고, 영속성 컨텍스트에 대해서 더 찾아서 공부해야겠다.


참고자료

profile
나만의 무언가를 가진 프로그래머가 되자

0개의 댓글