연관관계

김민지·2022년 10월 24일
0

JPA

목록 보기
11/27
post-thumbnail

연관관계 매핑 시 고려사항 3가지

  • 다중성(다대일, 일대다, 대대다, 일대일)
  • 단방향, 양방향
  • 연관관계의 주인

딜레마 : 멤버의 team을 바꿨을 때 member의 team_id를 바꿀 것인가? team의 members가 바뀌었을때 member의 team_id를 바꿀것인가?(연관관계의 주인을 정한다는 것의 의미)

→ 둘다 맞는말이지만 디비 입장에서는 member table의 team_id값만 바뀌면 되는거임

→ 그래서 이 둘중 우선순위를 정하는게 연관관계의 주인을 정하는 일이다.

다대일 단방향

public class Member {
  ...
  
    **@ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;**
  
  ...
}
  • 다쪽에 일에 대한 멤버변수를 만들어준다
  • 왜냐하면 외래키를 연관관계의 주인이(다)쪽이 가지고 있음. joincolumn으로 fk의 이름을 명시해준다. 그리고 fk랑 매핑하기 위해서 manytoone어노테이션을 사용해준다

양방향 매핑

  • fk하나만 집어넣으면 table에서는 양방향 참조가 가능하다
  • 하지만 문제는 객체다.
  • team에다가 member list로 초기화를 해준 뒤 넣어주고 OneToMany어노테이션을 추가해주고 mappedby=”team”을 추가해준다
  • 양방향일때 mappedby가 필요함. fk를 가지고 있는쪽이 연관관계의 주인이고 그 반대편엔 단방향일경우 연관관계의 주인클래스를 참조하는 멤버변수가 존재하지 않는데 양방향인경우 mappedby를 통해 멤버변수를 추가시켜줄수있음
  • team에 아래와 같은 코드를 추가해주면 양방향이 된다
public class Team {
    ...

    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<>();

    ...
}

일대다 단방향

일대다 단방향

  • 테이블 일대다 관계에서도 다쪽에 외래키가 있다 →joincolumn의 인자로 fk를 넣어줄때 다에 있는 fk가 들어갈 것이다. 즉, 일의 pk가 들어갈 것이다.
  • @joinColumn을 사용안하면 조인테이블 방식이 사용되도록 설정되어있다
@Entity
public class Team {
    ...

    @OneToMany
    @JoinColumn(name = "TEAM_ID")
    private List<Member> members = new ArrayList<>();

    ...
}

보면 team에는 fk가 없잖아 그래서 team의 member에 member를 추가해준다고 해도 테이블엔 영향이 없기때문에 update쿼리까지 같이 나가는 것

콘솔창을 보면


member insert

team insert

update쿼리

이렇게 나간다.

→ team만 손댔는데 왜 member에 대한 update쿼리가 나가는거지?

→ 일에서 컬렉션을 수정하면 그 컬렉션과 연관된 다의 외래키 컬럼이 수정된다.

일대다 단방향 매핑 단점

일대다 양방향

member에 다음을 추가해주면 양방향이 가능해진다

update쿼리

일대다 양방향에서 update쿼리가 나가는 이유

일대다 양방향에서 mappedby를 못쓰는 이유

  • 양방향 매핑에서 @OneToMany는 연관관계의 주인이 될 수 없다.
  • 왜냐하면 관계형 데이터베이스의 특성상 일대다, 다대일 관계는 항상 다 쪽에 외래 키가 있기때문이다.
  • 일대다 단방향 매핑 반대편에 같은 외래 키를 사용하는 다대일 단방향 매핑을 읽기 전용으로 하나 추가함으로써 일대다 양방향을 구현하는것이다.
  • 이런 매핑은 공식적으로 존재 X
  • @JoinColumn(insertable=false, updatable=false)
  • 읽기 전용 필드를 사용해서 양방향처럼 사용하는 방법

JOIN CLOUMN이란

  • JoinColumn은 어노테이션이 붙은 필드의 엔티티를 추적해서 그 엔티티의 PK를 Join 시켜준다.

양방향 매핑의 규칙

  • 객체의 두 관계중 하나를 연관관계 주인으로 지정(외래키가 있는 곳을)
  • 연관관계의 주인만이 외래 키를 관리한다(등록, 수정)
  • 주인이 아닌 쪽은 읽기만 가능하다
  • 주인은 mappedBy 속성을 사용하지 않는다
  • 주인이 아니면 mappedBy 속성으로 주인을 지정한다

그래서

위의 코드처럼 setMembers를 해봤자 아무일도 안일어난다. 읽기만 가능하기 때문

하지만 양방향 매핑관계에서 양쪽에 모두 값을 넣어줘야하는 이유

만약에 이렇게 flush랑 clear를 안해준다면..

23번째 줄까지의 정보를 가진 team객체 에서 members를 얻어내는거니까

48번째줄에 아무것도 안나오게 된다

→ 순수 객체 상태를 고려해서 항상 양쪽에 값을 설정하자

→ 연관관계 편의 메서드를 정의하자

연관관계 편의 메서드란?

다음과 같이 setTeam을 할 때 상대편에도 자기자신에 대한 정보를 넘겨주는 것

일대일 관계

  • 일대일관계는 아무테이블에나 외래키를 선택가능하다
  • 외래키에 데이터베이스 유니크 제약조건(설정된 칼럼에는 중복된 값이 들어가지 못하게 하는 것)을 추가해줘야한다

일대일관계에서 어떤 테이블에 외래키를 설정할 것인가?

  • 만약 locker쪽에 fk를 넣으면. member:locker가 일대다관계가됐을때 수정이 수월하다
  • member에 fk를 넣으면 member자체가 자주쓰이는데 자주쓰이는애로 locker까지 접근할 수 있으니 좋음

→ 장단점이 있다는 얘기

일대일 매핑 방법(단방향)

  • fk가 있는 쪽에 다음과 같이 onetoone과 joincolumn을 설정해준다

일대일 양방향하는법

  • 각 클래스에 OneToOne어노테이션을 추가해준다
  • fk를 가지고 있는 클래스에는 JoinColumn어노테이션도 추가해준다
  • joincolumn반대편에는 onetoone어노테이션옆에 mappedby를 추가해준다

다대다

다대다 매핑하는 법

  • 상품 ↔ member

다대다를 지양해야하는 이유

  • 중간 테이블에는 매핑정보만 들어가고 추가 데이터를 넣는 것이 불가능하다. 에를들어 멤버랑 상품에 대한 중간테이블에는 멤버랑 상품을 매핑하면서 예를들어 상품구매시간 같은 추가 데이터를 못넣는다
  • 중간 테이블이 숨겨져 있기 때문에 쿼리가 예상하지 못하는 형태로 나간다.
  • 실무 비즈니스는 복잡해서 ManyToMany로 풀 수 있는게 거의 없다고 보면 된다.

무한루프에 안빠지는 양방향

 public void addMember(Member member){
        this.memberList.add(member);
        if(member.getTeam() !=this){
            member.setTeam(this);
        }
    }
 public void setTeam(Team team){
        this.team = team;
        if(!team.getMemberList().contains(this)) team.getMemberList().add(this);
    }
profile
안녕하세요!

0개의 댓글