연관 관계 - 단방향, 양방향

신상현·2021년 2월 11일
0

Spring Boot와 JPA

목록 보기
11/16

단방향 연관 관계

@ManyToOne

다중성을 나타내는, 다대일 관계를 매핑하는 어노테이션으로 작성 필수

@JoinColumn

  • 외래 키를 매핑할 때 사용한다.
  • name 속성은 매핑할 외래 키 이름을 지정한다.
  • 생략하는 경우에는 필드명(team) + _ + 참조하는 테이블의 컬럼명(TEAM_ID)
    => team_TEAM_ID로 테이블에 MEMBER 테이블에 컬럼이 생긴다.

객체 관계 매핑

  • Member
@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
    }

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

양방향 관계


테이블 연관 관계는 원래 외래 키를 사용해 양방향 조회가 가능하기 때문에 수정할 필요가 없다.

@OneToMany

  • 다중성을 나타내는, 일대다 관계를 매핑하는 어노테이션으로 작성
  • 하나의 객체가 여러 개의 다른 객체를 참조할 수 있으므로, 컬렉션을 사용해야한다.
  • mappedBy 속성은 양방향 관계일 때 사용하는데, 반대쪽 매핑의 필드 이름을 값으로 넘겨주면 된다.

객체 관계 매핑

Team 객체도 Member들에 대한 참조를 컬렉션을 활용해서 갖는다.

  • Member - 수정 사항이 없다.
@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;
    }
}
  • Team - 일대다 관계 매핑
@Entity
public class Team {
    
    @Id
    @Column(name = "TEAM_ID")
    private String id;
    
    private String name;
    
    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<Member>();
}

연관 관계의 주인

mappedBy는 왜 필요하지?

연관 관계의 주인을 지정하는 것은 외래 키 관리자를 지정하는 것이다.

  • JPA에서는 두 객체 연관 관계 중 하나를 정해서 테이블의 외래 키를 관리해야 한다.
    즉, 두 연관 관계 중 하나를 주인으로 정해야 한다.

  • 연관 관계의 주인만이 DB 연관 관계와 매핑되고, 외래키를 관리할 수 있다.
    반면에, 주인이 아닌 쪽은 조회만 가능하다.

  • 주인이 아니면, mappedBy 속성으로 주인을 지정해줘야 한다.
    mappedBy 속성의 값으로 반대쪽 매핑의 필드 이름으로 주인으로 지정한다.

어떻게 주인을 결정하지?

연관 관계의 주인은 테이블의 외래 키가 있는 곳으로 정해야 한다.

테이블 연관 관계에서 외래 키 TEAM_IDMEMBER 테이블에 있다.
Member.team 을 주인으로 선택하면, 자기 테이블에 있는 외래 키를 관리한다.
하지만 Team.Members를 주인으로 설정하면 떨어져서 외래 키를 관리해야 한다.
주의! 비즈니스적으로 더 중요한 역할을 하는 엔티티를 주인으로 설정하면 안된다!


양방향 연관관계 설정 시 주의점

양 쪽 모두에 데이터를 입력해줘야 한다.

연관 관계의 주인만이 외래 키의 값을 변경할 수 있다.
즉, 주인이 아닌 곳에서 데이터를 입력해도 변경 되지 않는다.
또한, 순수한 객체 관계까지 고려한다면, 양쪽에 모두 데이터를 입력해주는 것이 좋다.

public void test_bothSide(){
    
    Team team1 = new Team("1", "팀1")
    em.persist(team1);
    
    // member1과 team1의 연관 관계 설정
    Member member1 = new member("1", "회원1")
    member1.setTeam(team1);
    team1.getMembers().add(member1);
    em.persist(member1)

    // member2와 team1의 연관 관계 설정
    Member member2 = new member("2", "회원2")
    member2.setTeam(team1);
    team1.getMembers().add(member2);
    em.persist(member2)
}

무한루프, 수정하는 상황을 고려해서 편의 메소드를 작성하자

양방향 연관 관계는 결국 양쪽 다 신경써야 한다.
실수로 둘 중 하나만 호출하는 일이 없게 두 코드를 하나인 것처럼 작성해두자!

1. 기존 팀 여부 확인 => 관계를 끊고, 반대편에서도 제거
2. 연관 관계의 반대편에도 추가하는 과정 확인

public void setTeam(Team team){
    
    // 기존에 팀이 있었다면, 기존 관계를 끊고 새로운 팀을 추가해야 한다.
    // 이렇게 하지 않으면, 연관 관계의 반대편에는 리스트에 기존 팀으로 기록된다.
    if(this.team != null){
        this.team.getMembers.remove(this);
    }
    
    // 연관 관계의 반대 쪽에도 추가해줌으로써 문제 발생을 최소화한다!
    this.team = team;
    team.getMembers().add(this);
}

profile
개발자 싱상형

0개의 댓글