채널톡 아이콘

인프런 커뮤니티 질문&답변

정준상님의 프로필 이미지

작성한 질문수 2

자바 ORM 표준 JPA 프로그래밍 - 기본편

양방향 참조 관계에서 `mappedBy` 를 생략하면 어떻게 될까요?

해결된 질문

작성

·

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 조회 내용입니다.

스크린샷 2025-04-07 18.28.49.png

 

위 상황에서 왜 member1, member2 의 팀 변경내용이 모두 반영되는지 알고 싶습니다.

 

이러한 경우 JPA 가 Member, Team 모두를 연관관계의 주인이라 인식하는 건가요?

아니면 JPA 가 Member -> Team, Team -> Member 두 참조를 서로 다른 연관관계(?) 라 해석해 동작하는 걸까요?

답변 2

0

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. 정준상님

이렇게 되면 Member -> Team, Team -> Member 두 참조가 서로 다른 연관관계로 해석됩니다.

JPA가 이렇게 잘못된 관계를 허용한다고 해도, 이런 관계는 사용하지 않아야 합니다.

감사합니다.

0

인프런 AI 인턴님의 프로필 이미지

안녕하세요, 인프런 AI 인턴입니다.

정준상님께서 질문하신 양방향 참조 관계에서 mappedBy 키워드를 생략했을 때 발생하는 상황에 대해 설명드리겠습니다.

JPA에서 양방향 연관관계를 설정할 때, mappedBy 속성을 사용하여 연관관계의 주인을 지정해 주어야 합니다. 연관관계의 주인은 데이터베이스의 FK(Foreign Key)에 영향을 미치는 엔티티를 의미하며, 주인은 @ManyToOne 또는 @OneToOne 쪽이 보통 사용됩니다.

그런데, mappedBy를 생략할 경우, JPA는 양쪽 모두를 독립적인 연관관계로 인식합니다. 즉, MemberTeam 간의 관계가 두 개가 존재하는 것으로 간주됩니다. 이는 관계를 유지하기 위해 Memberteam 필드와 Teammembers 리스트 모두 update나 insert가 되도록 암시적인 쿼리를 생성합니다. 그래서 두 객체가 모두 업데이트됩니다.

예상된 결과

  • member1.team = team1;Member 엔티티에서 FK를 업데이트합니다.
  • team2.members.add(member2);은 새로운 update 쿼리를 만들어 해당 관계를 Member 테이블에 반영합니다.

쿼리 결과

위 결과에서 보시듯이, Member들이 다른 Team에 연결된 상태로 잘 반영되어 있습니다.

정리

mappedBy가 없을 때, JPA는 각 관계를 별개의 것으로 판단하고 서로 다른 연관관계로 처리합니다. 이로 인해 양측 모두 업데이트가 가능한 상태가 되는 것입니다.

참고

정리하자면, mappedBy를 통해 명시적으로 주인을 설정하지 않으면 의도하지 않은 DB 입력과 업데이트가 발생할 수 있으므로, 반드시 설정해야 합니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.

현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

정준상님의 프로필 이미지
정준상
질문자

JPA 나 Hibernate 문서에서 이런 내용이 설명된 부분이 있을까요?
더 깊게 공부해 보고 싶습니다.

혹시 관련 자료나 링크 있으시면 공유해 주세요...!