둘셋 개발!

[JPA] 다대일에서 update쿼리가 잘못 나갈 수 있다..! 본문

JPA

[JPA] 다대일에서 update쿼리가 잘못 나갈 수 있다..!

23 2023. 9. 22. 11:50

영속화 순서에 따라 예상치 못한 update쿼리가 나갈 수 있다.

다음 예시를 보자!!

 

코드

Member.class

package hellojpa.entitiy;

import javax.persistence.*;

@Entity
public class Member {

    @Id
    @GeneratedValue
    private Long id;
    private String username;

    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;

    // getter, setter
}

 

Team.class

package hellojpa.entitiy;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

@Entity
public class Team {


    @Id @GeneratedValue
    private Long id;

    private String name;

    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<>();

    // getter, setter

}

이렇게 두 엔티티를 만들고

 

EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();


Member memberA = new Member();
memberA.setUsername("uni");
memberA.setAge(23);

Member memberB = new Member();
memberB.setUsername("kelly");
memberB.setAge(25);

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

memberA.setTeam(team);
memberB.setTeam(team);
team.addMember(memberA);
team.addMember(memberB);

em.persist(memberA);
em.persist(memberB);
em.persist(team);

System.out.println("----------------플러시");
em.flush();

System.out.println("---------------커밋");
tx.commit();

이걸 실행하면

 

Hibernate: 
    call next value for hibernate_sequence
Hibernate: 
    call next value for hibernate_sequence
Hibernate: 
    call next value for hibernate_sequence
----------------플러시&클리어
Hibernate: 
    /* insert hellojpa.entitiy.Member
        */ insert 
        into
            Member
            (age, TEAM_ID, username, id) 
        values
            (?, ?, ?, ?)
Hibernate: 
    /* insert hellojpa.entitiy.Member
        */ insert 
        into
            Member
            (age, TEAM_ID, username, id) 
        values
            (?, ?, ?, ?)
Hibernate: 
    /* insert hellojpa.entitiy.Team
        */ insert 
        into
            Team
            (name, id) 
        values
            (?, ?)
Hibernate: 
    /* update
        hellojpa.entitiy.Member */ update
            Member 
        set
            age=?,
            TEAM_ID=?,
            username=? 
        where
            id=?
Hibernate: 
    /* update
        hellojpa.entitiy.Member */ update
            Member 
        set
            age=?,
            TEAM_ID=?,
            username=? 
        where
            id=?
---------------커밋

 

이렇게 insert문 3개, update2문 2개가 나갔다.

사실 3개의 데이터를 저장하는 것이기 때문에 3개의 쿼리문을 예상했지만 총 5개의 쿼리가 나갔다.

 

update문이 나간 이유는 한 마디로 jpa의 더티체킹 때문이다.

 

내가 간과하고 있었던 점은 엔티티의 식별자를 얻는 시점이였다.

엔티티의 식별자를 자동생성방식을 사용해서 할당했기 때문에 (@GeneratedValue 사용)

엔티티를 생성할 때 식별자를 얻는 것이 아니라 엔티티를 영속화 할 때! 즉, 위의 코드에서 em.persist()를 할 때

DB를 통해서 식별자를 얻는 것이였다.

 

따라서 memberA와 memberB를 영속화 할 당시에는 team의 식별자를 모르기 때문에 team의 값이 null이였고,

team을 영속화 할 때 (  em.persist(team)  )  memberA와 memberB의 team의 id값이 생기면서 

이때 JPA에서는 인스턴스 값이 바뀌었기 때문에 변경감지를 통해 update쿼리를 날렸던 것이다.

 

 

그렇다면 team을 먼저 영속화 시킨다면??

EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();


Member memberA = new Member();
memberA.setUsername("uni");
memberA.setAge(23);

Member memberB = new Member();
memberB.setUsername("kelly");
memberB.setAge(25);

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

memberA.setTeam(team);
memberB.setTeam(team);
team.addMember(memberA);
team.addMember(memberB);

em.persist(team);      // 가장 먼저 영속화!!
em.persist(memberA);
em.persist(memberB);
//em.persist(team);

System.out.println("----------------플러시");
em.flush();

System.out.println("---------------커밋");
tx.commit();

em.persist(memberA), em.persist(memberB) 시점에

team의 members의 memberA, memberB각각의 id가 null에서 의미있는 값이 들어온다.

 

값이 바뀌어서 변경감지로 인해 update 쿼리가 나갈 것 같지만 아니다!!

mappedBy를 다른 객체의 필드로 했기때문에 team에서는 읽기만 가능 할 뿐이기 때문에 값이 바뀌어도 update 쿼리가 나가지 않는다.

 

결론

영속화 순서에 따라 예기치 못한 update쿼리가 나갈 수 있으니, 객체의 영속화 순서도 신경써야 한다.

영속성 전의를 사용해서 특정 엔티티를 영속 상태로 만들 때 연관된 엔티티도 함께 영속화 하는 방법도 있긴 하다!!

 

(생각보다 영속화에 대해서 신경을 쓸 것이 많다..!!)