DB/JPA

[📗자바 ORM 표준 JPA 프로그래밍] 양방향 연관관계 매핑 및 주인 정하기

개발자 May 2024. 11. 13.

지난 포스팅에서는 단방향 연관관계에 알아보았다. 이어서, 조금 더 복잡한 개념인 양방향 연관관계에 대해 알아보자.

5.3 양방향 연관관계

양방향 연관관계는 양쪽 엔티티가 서로를 참조하는 관계를 의미한다. 예를 들어, 회원(Member)이 하나의 팀(Team)에 속해 있다고 가정할 때, 단방향 관계에서는 MemberTeam을 참조하는 구조였다. 이제 팀도 해당 팀에 속한 회원들을 참조하도록 설정하여 양방향 관계를 만들어 보자.

  • 회원-팀: 다대일(N:1) 관계 / 회원 → 팀 (Member.team)
  • 팀-회원: 일대다(1:N) 관계 / 팀 → 회원 (Team.members)
@Entity
public class Member {
    @Id
    @Column(name = "MEMBER_ID")
    private String id;
    private String username;

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

    public void setTeam(Team team) {
        this.team = team;
    }
    // getters, setters 생략
}

@Entity
public class Team {
    @Id
    @Column(name = "TEAM_ID")
    private String id;
    private String name;

    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<>();
    // getters, setters 생략
}
  • 회원(Member) 엔티티는 기존과 동일하게 @ManyToOne 관계를 통해 team을 참조하고, (Team) 엔티티는 회원 컬렉션(members)을 가지고 일대다 관계를 설정했다.
  • @OneToMany(mappedBy = "team"): mappedBy 속성은 양방향 관계에서 반대쪽 매핑 필드를 지정한다. 즉, Member 엔티티의 team 필드가 외래 키를 관리하므로, Team.membersmappedBy로 설정된다.

5.4 연관관계의 주인 (Owner)

양방향 연관관계에서는 외래 키를 실제로 관리하는 주체를 설정해야 한다. 연관관계의 주인은 데이터베이스에서 외래 키의 값을 변경할 수 있는 엔티티이며, 반대쪽은 읽기 전용으로 설정된다.

  • 주인 엔티티가 외래 키를 관리하며, 이곳에서 외래 키를 변경하면 데이터베이스에 반영된다.
  • 주인 엔티티는 mappedBy 속성을 사용하지 않는다. 반대로 주인이 아닌 엔티티는 mappedBy를 사용하여 주인을 지정한다.

주인 설정의 예

위 예제에서는 외래 키를 가진 Member.team 필드가 주인이 된다. Team.members는 외래 키를 가지지 않으므로 주인이 아니며, mappedBy를 통해 연관 필드(team)를 지정하여 양방향 관계를 설정한다.


5.5 양방향 연관관계 저장

연관관계의 주인이 아닌 곳에 값을 입력해도 데이터베이스에는 반영되지 않는다. 예를 들어, Team.members.add(member)를 호출해도 DB의 외래 키에는 영향을 주지 않는다. 반드시 주인 필드(Member.team)에 값을 설정해야 한다.

public void testSave() {
    Team team = new Team();
    team.setId("team1");
    team.setName("팀1");
    em.persist(team);

    Member member = new Member();
    member.setId("member1");
    member.setUsername("회원1");
    member.setTeam(team); // 주인 필드에 값 설정
    em.persist(member);

    team.getMembers().add(member); // 연관 관계 추가 (주인에 반영되지 않음)
}

이 코드에서 연관관계의 주인인 member.setTeam(team);만 데이터베이스에 영향을 준다.


5.6 양방향 연관관계의 주의점과 편의 메서드

양방향 관계에서는 객체의 참조를 양쪽에 모두 설정해야 탐색이 원활해진다. 만약 한쪽에만 값을 설정하면, 다른 방향에서 연관 객체에 접근할 수 없다. 따라서 연관관계 편의 메서드를 사용하는 것이 좋다.

연관관계 편의 메서드 예제

public class Member {
    // 기존 코드 생략

    public void setTeam(Team team) {
        this.team = team;
        team.getMembers().add(this); // 편의 메서드로 양쪽에 모두 값 설정
    }
}

편의 메서드를 사용하면, 연관관계를 설정할 때 양쪽 엔티티의 필드에 동시에 값을 설정할 수 있다.

팀 변경 시 주의 사항

편의 메서드로 팀을 변경할 때는 기존 팀에서 해당 회원을 제거하는 로직을 추가해야 한다.

public void setTeam(Team team) {
    if (this.team != null) {
        this.team.getMembers().remove(this); // 기존 팀에서 제거
    }
    this.team = team;
    team.getMembers().add(this); // 새로운 팀에 추가
}

이 코드를 통해, 회원의 팀을 변경할 때 기존 팀과의 연관관계도 제거하여 일관성을 유지할 수 있다.


정리

양방향 연관관계는 단방향에 비해 설정과 관리가 더 복잡하다. 연관관계의 주인을 설정하고, 주인 필드에서 외래 키를 관리해야 하며, 객체 간 참조를 양쪽에 모두 설정해야 한다.

양방향 매핑이 복잡하다면 먼저 단방향 매핑을 설정하고, 그 이후에 필요한 경우 양방향 관계를 추가하는 방법으로 접근하는 것이 좋다.


Reference

[도서]자바 ORM 표준 JPA 프로그래밍(김영한 저)

댓글