해결된 질문
작성
·
56
0
안녕하세요. JPA 학습중인 학생입니다.
양방향 참조 관계에서 여러 방법으로 연습하던 중,
예상치 못한 행동을 발견해 왜 이렇게 작동하는지 궁금해서 질문드립니다.
연습한 코드입니다.
// Team - Member 는 1:N
@Entity
class Member {
@Id @GeneratedValue
private Long id;
private String name;
// 편의상 public
@ManyToOne
@JoinColumn(name = "TEAM_ID")
public Team team;
public Member() {
}
public Member(String name) {
this.name = name;
}
}
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
// 편의상 public
@OneToMany // 원래는 mappedBy = "team" 으로 owner 를 설정해야 함.
@JoinColumn(name = "TEAM_ID")
public final List<Member> members = new ArrayList<>();
public Team() {
}
public Team(String name) {
this.name = name;
}
}
강의에서 배웠듯, 양방향 참조관계에선 mappedBy = ...
로 관계의 주인을 설정해야 합니다.
그런데 만약 이를 생략하면 어떻게 작동할까 호기심이 생겨 아래의 코드를 실행시켰습니다.
EntityManager em = getEm(); // 편의상 코드 생략
em.getTransaction().begin();
Member member1 = new Member("member1");
Member member2 = new Member("member2");
Team team1 = new Team("team1");
Team team2 = new Team("team2");
// 편의상 코드 생략
// 단순 em.persist( given ) 을 수행합니다.
persistAll(member1, member2, team1, team2);
em.flush(); // DB 반영
System.out.println("============== AFTER FLUSH ==============");
member1.team = team1; // [1] : mem1 의 팀을 t1 으로 설정
team2.members.add(member2); // [2] : mem2 의 팀을 t2 로 설정
em.getTransaction().commit(); // Member - Team 연관관계 변경 내용 commit
em.close();
System.out.println("============== END ==============");
제 예상은 실행시 오류가 발생하거나 [1]
내용만 반영되는 것이었습니다.
하지만 실행하니 [1]
, [2]
모두 반영되는 것을 확인하였습니다.
DDL & 출력 내용
Hibernate:
create table Member (
id bigint not null,
name varchar(255),
TEAM_ID bigint,
primary key (id)
)
Hibernate:
create table Team (
id bigint not null,
name varchar(255),
primary key (id)
)
Hibernate:
alter table Member
add constraint FKl7wsny760hjy6x19kqnduasbm
foreign key (TEAM_ID)
references Team
...
============== AFTER FLUSH ==============
Hibernate:
/* update
scripts.entities.Member */ update
Member
set
name=?,
TEAM_ID=?
where
id=?
Hibernate:
/* create one-to-many row scripts.entities.Team.members */ update
Member
set
TEAM_ID=?
where
id=?
============== END ==============
실행 후 DB 조회 내용입니다.
위 상황에서 왜 member1
, member2
의 팀 변경내용이 모두 반영되는지 알고 싶습니다.
이러한 경우 JPA 가 Member
, Team
모두를 연관관계의 주인이라 인식하는 건가요?
아니면 JPA 가 Member -> Team
, Team -> Member
두 참조를 서로 다른 연관관계(?) 라 해석해 동작하는 걸까요?
답변 2
0
안녕하세요. 정준상님
이렇게 되면 Member -> Team, Team -> Member 두 참조가 서로 다른 연관관계로 해석됩니다.
JPA가 이렇게 잘못된 관계를 허용한다고 해도, 이런 관계는 사용하지 않아야 합니다.
감사합니다.
0
안녕하세요, 인프런 AI 인턴입니다.
정준상님께서 질문하신 양방향 참조 관계에서 mappedBy
키워드를 생략했을 때 발생하는 상황에 대해 설명드리겠습니다.
JPA에서 양방향 연관관계를 설정할 때, mappedBy
속성을 사용하여 연관관계의 주인을 지정해 주어야 합니다. 연관관계의 주인은 데이터베이스의 FK(Foreign Key)에 영향을 미치는 엔티티를 의미하며, 주인은 @ManyToOne
또는 @OneToOne
쪽이 보통 사용됩니다.
그런데, mappedBy
를 생략할 경우, JPA는 양쪽 모두를 독립적인 연관관계로 인식합니다. 즉, Member
와 Team
간의 관계가 두 개가 존재하는 것으로 간주됩니다. 이는 관계를 유지하기 위해 Member
의 team
필드와 Team
의 members
리스트 모두 update나 insert가 되도록 암시적인 쿼리를 생성합니다. 그래서 두 객체가 모두 업데이트됩니다.
member1.team = team1;
은 Member
엔티티에서 FK를 업데이트합니다.team2.members.add(member2);
은 새로운 update 쿼리를 만들어 해당 관계를 Member
테이블에 반영합니다.위 결과에서 보시듯이, Member
들이 다른 Team
에 연결된 상태로 잘 반영되어 있습니다.
mappedBy
가 없을 때, JPA는 각 관계를 별개의 것으로 판단하고 서로 다른 연관관계로 처리합니다. 이로 인해 양측 모두 업데이트가 가능한 상태가 되는 것입니다.
정리하자면, mappedBy
를 통해 명시적으로 주인을 설정하지 않으면 의도하지 않은 DB 입력과 업데이트가 발생할 수 있으므로, 반드시 설정해야 합니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.
JPA 나 Hibernate 문서에서 이런 내용이 설명된 부분이 있을까요?
더 깊게 공부해 보고 싶습니다.
혹시 관련 자료나 링크 있으시면 공유해 주세요...!