[Java] Why, How - JPA save메서드로 UPDATE 쿼리가 실행되는 이유. (Transactional 메서드 내에서는 업데이트할 때 save 메서드가 필수가 아니다?)

하쮸·2025년 2월 2일

Error, Why, What, How

목록 보기
14/68

이때까지 save()메서드는 INSERT쿼리만 실행시켜주는 줄 알았는데
UPDATE쿼리도 수행 가능하다는 점, 그리고 트랜잭션 메서드 내에서 업데이트를 하기 위해 save()를 호출하는 것이 필수는 아니라는 점을 알게 돼서 정리한 글.


1. save() 메서드.

  • 일반적으로 save() 메서드INSERT 쿼리를 수행해서 데이터를 저장하는 역할을 함.
    • PK, 즉 ID가 없을 경우에 INSERT쿼리를 실행함.
  • 언제 save() 메서드UPDATE쿼리를 수행할까?
    • PK, 즉 ID가 있는 기존 데이터를 저장할 때 UPDATE 쿼리를 실행함.

1-1. 백문이 불여일견.

  • INSERT
    • 새로운 데이터가 저장됨.
    • PK, 즉 ID가 없던 객체.
      • ID를 따로 지정하지 않아도 Entity 클래스의 설정으로 인해
        처음에 id에 null값으로 생성되고 save() 메서드를 호출하면 id가 자동으로 생성됨.
  • UPDATE
    • 회원 정보수정 페이지에서 사용자가 수정한 값을 처리하는 메서드.
    • 즉, 새로운 데이터가 아니라 기존에 저장되어 있던 데이터(PK, 즉 ID가 존재).
public void update(MemberDTO memberDTO) {
        memberRepository.save(MemberEntity.updateMemberEntity(memberDTO));
    }

  • 위 이미지처럼 UPDATE쿼리가 실행되었음.

2. Transactional 메서드 내에서는 업데이트할 때 save 메서드가 필수가 아님.

    @Transactional
    public void update(MemberDTO memberDTO) {
        Optional<MemberEntity> optionalMember = memberRepository.findById(memberDTO.getId());
        if (optionalMember.isPresent()) {
            MemberEntity member = optionalMember.get();
            member.setMemberName(memberDTO.getMemberName());
            member.setMemberPassword(memberDTO.getMemberPassword());
        } else {
            throw new IllegalArgumentException("회원이 존재하지 않음.");
        }
    }

  • 데이터를 조회하고 세터로 데이터의 값만 바꿨을 뿐인데 save()메서드가 없었음에도 불구하고 UPDATE쿼리가 실행된 것을 확인할 수 있음.
    • @Transactional에노테이션은 필수임.
      @Transactional에노테이션이 없다면 데이터가 변하지 않음.
  • 업데이트가 가능한 이유는 JPA의 영속성 컨텍스트 때문임.
    • 트랜잭션 메서드 내부에서 findById()를 통해 데이터(즉 Entity)를 조회하면, 해당 엔터티는 JPA의 영속성 컨텍스트에 의해 관리됨.
    • 즉, 속성 값을 변경하는 것만으로도 자동으로 데이터베이스에 반영됨.

3. 영속성 컨텍스트(Persistence Context)

  • Hibernate와 같은 영속성 제공자는 애플리케이션에서 엔터티의 생명 주기를 관리하기 위해 영속성 컨텍스트(Persistence Context)를 사용함.
  • 공식정의.
    • EntityManager 인스턴스는 영속성 컨텍스트와 연결됨.
    • 영속성 컨텍스트는 엔터티 인스턴스의 집합이며, 하나의 영속성 컨텍스트 내에서 특정 엔터티 ID에 대한 고유한 엔터티 인스턴스가 존재함.
    • 영속성 컨텍스트는 엔터티 인스턴스를 관리하고 그들의 생명 주기를 추적하고 EntityManager API를 사용하여 엔터티를 생성 및 삭제하고, PK(기본 키)로 조회하며, 엔터티에 대한 쿼리를 실행할 수 있음.
  • 영속성 컨텍스트란?

    • 영속성 컨텍스트는 1차 캐시(First-Level Cache)로, 엔터티를 데이터베이스에서 가져오거나 저장할 때 활용됨.
    • 즉, 애플리케이션과 데이터베이스 사이에서 중간 역할을 함.
  • 특징.

    • 영속성 컨텍스트는 관리되는 엔터티의 변경 사항을 추적함.
    • 트랜잭션 중에 엔터티가 변경되면 해당 엔터티는 Dirty 상태로 표시.
      • 트랜잭션이 완료되면 변경된 내용이 데이터베이스에 자동으로 반영(Flush)됨.
  • EntityManager와 영속성 컨텍스트

    • EntityManager는 우리가 영속성 컨텍스트와 상호 작용할 수 있도록 해주는 인터페이스.
    • 즉, EntityManager를 사용할 때 우리는 실제로 영속성 컨텍스트와 소통하는 것.
  • 만약 엔터티가 변경될 때마다 즉시 데이터베이스와 동기화된다면, 데이터베이스 호출이 지나치게 많아져 성능이 저하될 것이고 영속성 컨텍스트는 이를 방지하는 역할을 함.


3-1. 유형.

  • 영속성 컨텍스트 유형(type)
    • 트랜잭션 범위 영속성 컨텍스트 (Transaction-Scoped Persistence Context).
      • 트랜잭션 범위 영속성 컨텍스트는 트랜잭션에 묶여 있음.
      • 즉, 트랜잭션이 종료되면 영속성 컨텍스트가 초기화되며, 엔터티는 데이터베이스에 저장.
      • 동작 방식.
        • 트랜잭션이 시작되면 EntityManager는 영속성 컨텍스트가 있는지 확인.
        • 없으면 새로운 영속성 컨텍스트를 생성.
        • 트랜잭션이 종료되면 변경된 엔터티가 데이터베이스에 반영(Flush)됨.
    • 확장 범위 영속성 컨텍스트 (Extended-Scoped Persistence Context).
      • 확장 범위 영속성 컨텍스트는 여러 트랜잭션에 걸쳐 유지될 수 있음.
      • 즉, 트랜잭션 없이도 엔터티를 영속성 컨텍스트에 저장할 수 있지만 트랜잭션이 없으면 변경 내용을 데이터베이스에 반영(Flush)할 수 없음.
      • 중요한 개념.
        • 확장 범위 영속성 컨텍스트는 Stateless Session Bean에서 사용될 때, 각 컴포넌트의 영속성 컨텍스트가 서로 독립적임.
        • Ex) Component A에서 한 엔터티를 영속화했다면, 같은 트랜잭션 내의 Component B에서는 해당 엔터티를 조회할 수 없음.
          (Component A와 Component B는 각각의 영속성 컨텍스트를 가짐.)

4. 참고.

profile
Every cloud has a silver lining.

0개의 댓글