• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    미해결

안녕하세요

23.02.01 19:06 작성 조회수 348

0

이 부분에서 em.persist(team) 을 한 후 회원 저장 구현을 한 후 em.persist(member) 를 했는데 회원 구현 이후에 em.persist(team) 를 해도 되나요? 회원 저장 구현 전에 em.persist(team) 를 해야 하는 이유가 있나요?

답변 1

답변을 작성해보세요.

4

안녕하세요.

같은 강의를 듣고 공부하고 있는 지나가던 나그네 입니다.

제가 공부한 내용을 토대로 답변을 한 번 드려보려고 합니다.

 

말씀주신 질문을 토대로 테스트 코드를 아래처럼 작성해봤습니다.

@Test
@DisplayName("member - team 테스트")
void memberTeamTest() {
    Team team = new Team();
    team.setName("team1");
    em.persist(team);

    Member member = new Member();
    member.setName("member1");
    member.setTeam(team);
    em.persist(member);

    // 아래 flush 와 clear 는 실제 insert 쿼리를 확인해보기 위해 추가했습니다.
    em.flush();
    em.clear();
    
    assertThat(member.getId()).isNotNull();
}

 

먼저 아래처럼 em.persist(team) 만 em.persist(member) 아래로 내려도 실제 동작 수행에는 크게 문제가 되지 않습니다.

@Test
@DisplayName("member - team 테스트")
void memberTeamTest() {
    Team team = new Team();
    team.setName("team1");

    Member member = new Member();
    member.setName("member1");
    member.setTeam(team);

    em.persist(member);
    em.persist(team);

    // 아래 flush 와 clear 는 실제 insert 쿼리를 확인해보기 위해 추가했습니다.
    em.flush();
    em.clear();
    
    assertThat(member.getId()).isNotNull();
}

 

하지만, 실제로 테스트를 실행해보면 update 쿼리가 하나 더 실행되실 겁니다.

이유는 영속성 컨텍스트에 데이터가 생성되는 시점이 달라서 인데요.

em.persist 는 객체를 영속성 컨텍스트에 영속화 하게 되는데, member를 먼저 영속화 하는 경우,

team 객체가 영속화 되어있지 않은 상태이기 때문에, team 객체에 id가 없습니다.

영속성 컨텍스트 내 1차캐시 상황을 그려보면 아래와 같을 것으로 예상이 되는데요.

image자바 상에서 team 이라는 객체는 생성됐지만, EntityManager에 의해 영속화 되기 전이기 때문에 id 는 null로 들어가게 될 것으로 예상됩니다.

그 다음에 team 을 영속화 하게 되면 아래처럼 1차 캐시가 변경될 것 입니다.

image이 상태에서 member 객체 내에 있는 team 객체가 영속화가 되었으니, team id 가 생성되었으므로

member가 참조하는 team에도 해당 id를 부여할 것으로 예상됩니다. 따라서 아래처럼 다시 1차캐시가 바뀌게 될 것입니다. ( 같은 트랜잭션 내에서 발생되어 변경감지로 간주되는 것으로 예상됩니다.)

image이 경우 에서 1차캐시에 있는 내용을 DB에 반영하면 SQL이 순차적으로 아래 처럼 발생하게 됩니다.

  1. INSERT INTO member(id, name, member_id) VALUES(1, "member1", null);

  2. INSERT INTO team(id, name) VALUES(2, "team1");

  3. UPDATE member SET team_id = 2 WHERE id = 1;

image

즉, 연관관계가 맺어져 있는 entity를 insert 하는 경우, 외래키를 가지는 연관관계의 주인을 나중에 insert 해야 insert 쿼리 2개가 발생하고,

질문 주신대로 순서를 바꿔서 persist 하는 경우에는 결과적으로 반영되는 데이터는 동일할지라도update 쿼리가 한 번 더 발생해서 3번의 쿼리가 발생되게 됩니다.

 

쿼리 2번이면 될 작업을 굳이 쿼리 3번으로 할 필요는 없겠죠? 이러한 이유 때문에 team 을 먼저 persist 하고 member 를 나중에 persist 한 것으로 예상됩니다.

 

저도 JPA 를 공부하는 입장이라 신뢰성(?)은 다소 떨어질 수 있지만, 제가 공부한 내용과 실제 테스트코드를 돌려본 결과로 인해 도출된 결과를 답변드리니 공부하실 때 참고되셨으면 좋겠습니다.

(혹시 영한님께서 이 답변을 보신다면.... 제 답변에 잘못된 내용 지적해주시면 감사드리겠습니다...!)