• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    해결됨

멤버와 팀의 영속화 순서를 뒤집었을 때(?) 생성되는 query에 대한 질문입니다!

24.03.01 13:44 작성 조회수 89

0

안녕하세요.

이상한 코드이긴 하지만 문득 궁금해서 테스트를 해봤습니다.

하이버네이트 버전은 6.4.2.Final

h2 데이터베이스 버전은 2.2.224

      Team team = new Team();
      team.setName("teamA");

      Member member = new Member();
      member.setUsername("memberA");
      member.setAge(49);
      member.setTeam(team);
      em.persist(member);
      em.persist(team);

이 경우 발생하는 query는 다음과 같습니다.

Hibernate: 
    /* insert for
        hellojpa.jpql.Member */

insert 
    into
        Member (age, TEAM_ID, username, id) 
    values
        (?, ?, ?, ?)
Hibernate: 
    /* insert for
        hellojpa.jpql.Team */
insert 
    into
        Team (name, id) 
    values
        (?, ?)
Hibernate: 
    /* update
        for hellojpa.jpql.Member */
update Member 
    set
        age=?,
        TEAM_ID=?,
        username=? 
    where
        id=?

제가 생각한 흐름은 이렇습니다.

  1. 멤버가 데이터베이스에 동기화된다

    • 팀이 멤버에 저장되어 있지만(객체 상으로), 팀 테이블에는 해당 데이터가 없다

    • 멤버의 외래키(team_id)가 아직 null이다

  2. 팀이 데이터베이스에 동기화된다

  3. 이제 멤버의 외래키를 업데이트 한다

    • 외래키만 업데이트 하면 될 것 같은데 외래키를 가진 레코드 전부를 수정한다

이 흐름이 맞을까요? 외래키만 업데이트 하는 게 아니라 멤버의 해당 레코드를 전부 업데이트하는 게 생각했던 것과 달라 질문드리게 됐습니다.

답변 미리 감사드립니다.

답변 1

답변을 작성해보세요.

2

안녕하세요. literate_t님

네, 설명해주신 흐름이 맞습니다. 여기서 발생하는 상황을 이해하기 위해서는 JPA와 하이버네이트가 어떻게 엔티티 관계를 관리하고, 데이터베이스에 반영하는지 알아볼 필요가 있습니다.

  1. 멤버가 데이터베이스에 동기화됩니다.
    처음에 Member 엔티티를 영속성 컨텍스트에 저장(persist)할 때, Member와 연관된 Team이 아직 데이터베이스에 존재하지 않기 때문에, MemberTEAM_ID는 null로 설정됩니다. 이 시점에서는 Member 엔티티만 데이터베이스에 삽입됩니다.

  2. 팀이 데이터베이스에 동기화됩니다.
    이어서 Team 엔티티를 영속성 컨텍스트에 저장할 때, 이제 데이터베이스에 Team 정보가 삽입됩니다.

  3. 이제 멤버의 외래키를 업데이트 합니다.
    Team이 영속성 컨텍스트에 저장되고 나서야 MemberTEAM_ID를 업데이트할 수 있게 됩니다. 이 업데이트는 Member 엔티티의 상태 변화를 감지하는 더티 체킹(dirty checking) 과정을 통해 일어납니다. 하이버네이트는 트랜잭션이 커밋되기 전에 변경된 모든 엔티티를 검사하고, 필요한 업데이트 SQL을 실행하여 데이터베이스의 상태를 최신으로 동기화합니다.

여기서 외래키만 업데이트 하는 것이 아니라 멤버의 해당 레코드를 전부 업데이트하는 부분에 대한 궁금증이 생길 수 있습니다. 이는 하이버네이트의 기본 동작 방식 때문입니다. 하이버네이트는 엔티티의 변경 사항을 감지할 때, 변경된 필드만을 대상으로 하는 '부분 업데이트'를 실행하지 않고, 엔티티의 모든 필드를 포함하는 '전체 업데이트'를 실행합니다. 이는 성능 최적화와 관련된 전략 중 하나로, 개발자가 명시적으로 최적화하지 않는 이상, 하이버네이트는 이 방식을 따릅니다.

만약 이 동작이 원치 않는 경우, 하이버네이트나 JPA 제공 기능을 이용해 업데이트 전략을 조정할 수 있습니다. 예를 들어, @DynamicUpdate 애노테이션을 사용하면 하이버네이트가 변경된 필드에 대해서만 업데이트 SQL을 생성하도록 할 수 있습니다. 하지만 이 기능은 신중하게 사용해야 하며, 모든 상황에서 성능 개선을 보장하지는 않습니다.

추가로 @DynamicUpdate 관련해서 다음 내용도 읽어보시면 도움이 되실거에요.

https://www.inflearn.com/questions/16697

감사합니다.

literate_t님의 프로필

literate_t

질문자

2024.03.03

와 감사합니다!