일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- @SubscribeMapping
- 알고리즘
- application layer
- 편향된 지수
- m:n
- Git
- 무한정 대기
- spring
- 프로그래머스
- 은행원알고리즘
- 오블완
- 파이썬
- 기본키 전략
- JPA
- @Autowired
- 컴파일 타임 상수
- 쉘 스크립트
- API
- allocationSize
- intelij spring config
- 백준
- BindingResult
- 리눅스
- DTO
- 커밋 되돌리기
- 티스토리챌린지
- JDBC
- 런타임 상수
- 쿠키
- compgen
- Today
- Total
둘셋 개발!
[JPA] 다대일에서 update쿼리가 잘못 나갈 수 있다..! 본문
영속화 순서에 따라 예상치 못한 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쿼리가 나갈 수 있으니, 객체의 영속화 순서도 신경써야 한다.
영속성 전의를 사용해서 특정 엔티티를 영속 상태로 만들 때 연관된 엔티티도 함께 영속화 하는 방법도 있긴 하다!!
(생각보다 영속화에 대해서 신경을 쓸 것이 많다..!!)
'JPA' 카테고리의 다른 글
[JPA] orphanRemoval 옵션 적용 버그 (0) | 2023.11.02 |
---|---|
[JPA] 일대다 양방향에서 '다'엔티티에 @JoinColumn을 생략하면 안되는 이유 (0) | 2023.09.22 |
[JPA] Primary Key 자동 생성 전략 (0) | 2023.09.15 |
[JPA 기본편] Jpa 구동방식 (0) | 2022.03.26 |
[API개발과 성능최적화-3] 컬렉션 조회 최적화 (0) | 2021.11.25 |