ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring boot] JPA save() & Dirty Checking
    프로젝트/아카이뷰 2024. 1. 19. 08:52

    JPA 메소드인 SAVE() 왜 궁금했을까❓

    JPA의 save()를 이용하여 객체를 데이터베이스에 저장하곤 했는데 Dirty Checking을 통해 저장하는 방식이 존재한다는 것을 알고 그 원리가 궁금해서 save()와 함께 학습해보려고 한다.

     

    SAVE() 동작 원리

    JPA에서 save()를 사용할 때, Spring Data JPA에서 제공하는 JpaRepository.save(T)를 사용하게 된다. 해당 메소드 코드를 보며 분석하고 그 원리를 학습하고자 한다.

    JpaRepository.save()

    • isNew() 메소드는 Entity가 새로운 것인지 아닌지 판단하는 메소드이다.
    • 일반적으로는 Entity ID의 null 여부에 따라 결정된다.
    • Entity에 @Id와 @GeneratedValue를 사용할 경우 ID는 persist()를 호출한 이후에 ID가 생성되어 원하는 로직으로 실행이 될 것이다.
    • 아래 두 번째 코드에서처럼 @Id만을 사용하여 Entity를 구성한다면 회원가입을 할 때 id 값이 null이 아닌 사용자의 ID 값으로 채워져 있을 것이다.
    • isNew()는 해당 값을 새로운 entity라고 인식하지 못하고 else 문으로 넘어가 merge()를 수행할 것이다. 이 과정에서 입력한 사용자 ID가 실제로 DB에 존재하는지 확인하기 위해 SELECT 쿼리를 날리게 된다.
    • 만약, DB에 있을 경우에는 entity에 입력된 정보로 merge가 발생하고 없을 경우에는 새로운 entity이므로 insert 쿼리가 발생하게 될 것이다.
    • 이처럼 save()에서 불필요한 SELECT가 발생하게 되므로 잘못된 방식일 것이다.
    // @Id, @GeneratedValue
    @NoArgsConstructor(access = AccessLevel.PROTECTED)
    public class User {
        @Id
        @GeneratedValue
        @Column(name = "id")
        private Long id;
        @Column(name = "email")
        private String email
        
        ...
    }
    // @Id
    @NoArgsConstructor(access = AccessLevel.PROTECTED)
    public class User {
        @Id
        @Column(name = "id", length = 16)
        private String id;
        ...
    }
    • 위와 같은 문제를 해결하기 위해서는 Persistable<String> 인터페이스를 상속하면 된다.
    • JPA 구현체 내부에서 작동했던 isNew()를 개발자가 오버라이딩하여 비교 대상을 직접 지정할 수 있다.
    @NoArgsConstructor(access = AccessLevel.PROTECTED)
    public class User implements Persistable<String> {
        @Id
        @Column(name = "id", length = 16)
        private String id;
     	
        ...
        
        @Override
        public boolean isNew() {
            return this.createdAt == null;
        }
    }
    • 객체가 만들어진 시간을 기준으로 하여 해당 값이 null이면 새로운 Entity로 인식하고 아니면 기존의 Entity로 인식하도록 지정하였다.

     

    Dirty Checking

    JPA의 Dirty Checking이란 EntityManager가 변경이 발생한 Entity를 자동으로 감지하여 DB에 반영하는 것이며 이미 영속화된 Entity를 대상으로만 작동한다. 따라서, 준영속상태이거나 비영속상태인 Entity에 대해서는 JPA Dirty Checking이 동작하지 않는다.
    • JPA는 Entity를 조회할 때, 해당 Entity의 상태를 기반으로 스냅샷을 만들어 트랜잭션이 종료되는 시점에 만들어 놓은 스냅샷과 비교하여 변경을 감지
    • 만약, 변경 사항이 존재한다면 Update 쿼리를 DB에 전달하여 수정이 이뤄지게 된다.
    @Override
    @Transactional
    public void updateUserDetail(String profileUrl, String introduce, String id) {
        1. User user = repository.getById(id);
        2. user.updateUserDetail(profileUrl, introduce);
    }
    1. User Entity를 조회할 때 User Entity 상태를 기반으로 하나의 스냅샵을 생성
    2. profile과 introduce 변경
    3. Transaction이 종료될 때, 변경이 감지되어 DB에 Update 쿼리 전송

    Dirty Checking에 의한 Update 쿼리문 출력

Designed by Tistory.