양방향 참조 관계에서 `mappedBy` 를 생략하면 어떻게 될까요?
안녕하세요. 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는 각 관계를 별개의 것으로 판단하고 서로 다른 연관관계로 처리합니다. 이로 인해 양측 모두 업데이트가 가능한 상태가 되는 것입니다.
참고
- 양방향 연관관계와 매핑에 관한 상세 내용 - JPA 기초 개념 및 연관관계 매핑의 주의사항을 배우실 수 있습니다.
정리하자면, mappedBy를 통해 명시적으로 주인을 설정하지 않으면 의도하지 않은 DB 입력과 업데이트가 발생할 수 있으므로, 반드시 설정해야 합니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.
벌크연산에서 member.getAge 호출 시 영속성 컨텍스트에서 데이터를 가져오는건가요?
0
33
2
inheritance startegy 선택시 고려사항
0
24
1
Entity 동등성 비교
0
23
1
실무 조언 관련 질문입니다.
0
48
1
H2데이터베이스 파일 생성
0
58
2
서브쿼리 강의에서 ALL 예시 관련 질문드립니다.
0
56
2
수정또는 삭제시 영속성 엔티티에 값이 무조건 있어야 하나요?
0
58
1
JPQL 메소드와 락
0
55
1
Delivery @OneToOne
0
61
1
17강 4~5분대 테이블 값 조회가 안됩니다.
0
96
2
UnsupportedOperationException 발생
0
86
3
H2 Database 연결이 안됩니다.
0
97
2
연관관계 매핑 질문드립니다.
0
87
2
h2데이터베이스 실행오류
0
110
2
persistence.xml
0
112
2
양방향 연관관계에서 연관관계의 주인(mappedBy)을 왜 꼭 정해야 하나요?
0
83
1
영속성 컨텍스트
0
69
1
JPA 프록시
0
98
1
Native Query와 MyBatis
0
71
1
영속성 컨텍스트는 어떤 메모리에 저장되는건가요?
0
90
1
임베디드 타입 예시 코드 관련 질문
0
121
3
명시적 조인에서 별칭을 주면 왜 객체에 접근할 수 있나요
0
96
3
인텔리제이 패키지 커서 단축키 질문
0
109
2
혹시 현재는 ID 데이터 타입이 String이면 안되나요?
0
149
1





