[JPA] 다양한 연관관계 매핑

Swim Lee·2021년 1월 29일
1

JPA

목록 보기
7/10
post-thumbnail

연관관계 매핑시 고려사항

  1. 다중성
  2. 단방향, 양방향
  3. 연관관계 주인

다중성

  • 다대일 : @ManyToOne
  • 일대다 : @OneToMany
  • 일대일 : @OneToOne
  • 다대다 : @ManyToMany (실무에서 사용X)

JPA에 있는 애노테이션들은 다 DB랑 매핑하기 위해서 있는 것.
따라서 RDB관점에서 다중성을 기준으로 고민하면 된다.

단방향, 양방향

🌟 테이블

  • 외래키 하나로 양쪽 조인 가능
  • 사실 방향이라는 개념이 없음

연관관계 주인

  • 테이블은 외래키 하나로 두 테이블이 연관관계 맺음
  • 객체 양방향 연관관계는 A➡B, B➡A처럼 참조가 2군데
  • 객체 양방향 관계는 참조가 2군데 있음. 둘중 테이블의 외래키를 관리할 곳을 지정해야함
  • 연관관계의 주인 : 외래키를 관리하는 참조 (등록 수정)
  • 주인의 반대편 : 외래키에 영향을 주지 않음, 단순 조회만 가능

다대일 (N:1)

다가 연관관계의 주인인 경우

다대일 단방향

  • 관계형 DB에서는 설계상 항상 다쪽에 외래키가 가야한다.
  • 가장 많이 사용하는 연관관계
  • 다대일의 반대는 일대다

다대일 양방향

  • 외래키가 연관관계의 주인
  • 양쪽을 서로 참조하도록 개발할 때 사용

일대다 (1:N)

일이 연관관계의 주인인 경우, 1 방향에서 외래키를 관리하겠다는 뜻
가능하다! 하지만 권장하지는 않는다 😗

일대다 단방향

  • Team은 Member 알고싶은데, Member 입장에서는 Team 알고싶지 않은 경우 (객체 입장에서 이런 설계 나올 확률 높음)
  • 객체입장에서는 무조건 다 쪽에 외래키가 들어가야한다. 일 쪽에는 외래키가 들어갈 수가 없다. DB설계상 불가능.
  • Team에 있는 List members 값을 바꿨을 때 TEAM_ID(FK)라는 다른 테이블에 있는 외래키를 업데이트 해줘야함.
  • Team에 있는 List members가 연관관계 주인이 되버리는 것이고, 외래키 관리는 연관관계 주인이 해줘야함

🟥 정리

  • 일대다 단방향은 일대다(1:N)에서 일(1)이 연관관계의 주인
  • 테이블 일대다 관계는 항상 다(N)쪽에 외래키가 있음
  • 객체와 테이블의 차이 때문에 반대편 테이블의 외래키를 관리하는 특이한 구조 (Team이 연관관계 주인이지만, Member에 있는 외래키 관리)
  • @JoinColumn을 꼭 사용해야함. 그렇지 않으면 조인 테이블 방식을 사용함 (중간에 테이블을 하나 추가함)

🟧 단점

  • 엔티티가 관리하는 외래키가 다른 테이블에 있음 (이거 자체도 이미 어마어마한 단점)
  • 연관관계 관리를 위해 추가로 UPDATE SQL 실행
Member member = new Member();
member.setUsername("member1");

em.persist(member);

Team team = new Team();
team.setName("teamA");

//여기 문제, TEAM테이블 아닌 MEMBER테이블에 추가로 업데이트 쿼리 날라감.
//다쪽이 연관관계 주인이었다면 그냥 insert 쿼리만 날라가고 끝났을 일임.
team.getMembers().add(member);

em.persist(team);

  • insert 쿼리 2번 + 추가 update 쿼리 1번 (성능상 손해)
  • 사실 성능상 손해는 그렇게 심하진 않다.
  • 이러한 모델링 안하는 이유는 다른 이유가 더 크다
  • Team 엔티티 수정했는데, MEMBER 테이블이 update된다
  • 헷갈릴 수 밖에 없음. 😵 특히 실무에서는 테이블 수십개가 엮여서 돌아가는 상황인데 더 헷갈림. 운영이 힘들어진다.

일대다 양방향

  • 굉장히 억지성 있긴 하지만 할 수는 있다.
  • 이런 매핑은 공식적으로 존재하지 x
  • @JoinColumn(insertable=false, updatable= false)
  • 읽기 전용 필드를 사용해서 양방향 처럼 사용하는 방법
  • 해당 속성 주지 않으면 연관관계 주인 2개인 것 처럼되서 그냥 망해버리는 거다 🤣
  • 그냥 다대일 양방향을 사용하자

일대일 (1:1)

  • 일대일 관계는 반대도 일대일
  • 주 테이블이나 대상 테이블 중에 외래키 선택 가능
    • 주 테이블에 외래키
    • 대상 테이블에 외래키
  • 외래키에 데이터베이스 유니크(UNI) 제약조건 추가
    • 일대다, 다대일이랑 비슷한데, 외래키에 유니크 제약조건이 추가됐다 보면 된다.
    • 물론 제약조건 걸지 않아도 상관없는데 그렇게되면 애플리케이션에서 관리를 엄청 잘해야한다.

주 테이블에 외래키 단방향

  • 다대일(@ManyToOne) 단방향 매핑과 유사

주 테이블에 외래키 양방향

  • 다대일 양방향 매핑처럼 외래키가 있는 곳이 연관관계의 주인
  • 반대편은 mappedBy 적용

대상 테이블에 외래키 단방향

  • Member 엔티티에 있는 참조로 (locker), 반대쪽 테이블 (LOCKER 테이블)에 있는 외래키를 관리할 수 있나?
  • NOPE
  • 아예 JPA에서 지원도 안되고 방법도 없다.
  • 단방향 관계는 JPA 지원 X
  • 양방향 관계는 지원

대상 테이블에 외래키 양방향

  • 말에 어폐가 있음
  • 그냥 대상 엔티티의 참조를 외래키와 매핑하면됨 🤣
  • 사실 일대일 주테이블 외래키 양방향 뒤집은 것이랑 똑같다
  • 정리를 하면 일대일 관계는 내 엔티티 있는 외래키 직접 관리해야한다.

일대일 정리

주테이블에 외래키

  • 주 객체가 대상 객체의 참조를 가지는 것처럼
  • 주 테이블에 외래키를 두고 대상 테이블을 찾는다
  • 객체지향 개발자가 선호
  • JPA 매핑이 편리
  • 장점 : 주 테이블만 조회해도 대상 테이블에 데이터가 있는지 확인 가능
  • 단점 : 값이 없으면 외래키에 null 허용

대상 테이블에 외래키

  • 대상 테이블에 외래키가 존재
  • 전통적인 데이터베이스 개발자가 선호
  • 장점 : 주 테이블과 대상 테이블을 일대일에서 일대다 관계로 변경할 때 테이블 구조를 유지
  • 단점 : 무조건 양방향으로 만들어야함, 단방향으로 JPA에서 지원안함.
    ➕ 프록시 기능의 한계로 지연로딩으로 설정해도 항상 즉시로딩 된다!

JPA 입장에서 프록시 객체를 만들려면, 해당 엔티티의 참조에 매핑되는 값이 테이블에 있는지 없는지 알아야한다. 지연로딩시 참조에 값이 있다면 프록시로 초기화하고, 값이 없으면 그냥 null 넣어주면 되기 때문! 만약 주테이블에 외래키 있다면, 주 엔티티의 참조변수에 매핑되는 값 있는지 확인하기 위해서는 주 테이블만 검색하면 되니까 다른 대상테이블 검색할 필요가 없어서 지연로딩이 가능.

BUT 대상테이블에 외래키 있는 경우, 주테이블 가져올때 해당 참조에 매핑되는 값 있는지 확인하기 위해서는 대상 테이블까지 검색해야한다. (주테이블에 외래키값이 없고, 외래키 값이 대상테이블에 있으니까, 매핑되는 값 확인하려면 대상테이블을 검색해야한다.)어차피 대상테이블을 뒤져야하기 때문에 사실상 지연로딩을 하더라도 즉시로딩이 되버린다. 따라서 지연로딩으로 세팅하는 것이 의미가 없다.

다대다 (N:M)

실무에서 사용하면 안되는 연관관계

  • 관계형 데이터베이스는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없다.

  • 연결테이블을 추가해서 일대다, 다대일 관계로 풀어내야한다.

  • 객체는 컬렉션을 사용해서 객체 2개로 다대다 관계 가능

  • 두 객체와 세 개의 테이블(매핑 테이블 포함)을 매핑해주는 것이 다대다 연관관계 매핑

  • @ManyToMany 사용
  • @JoinTable로 연결 테이블 지정해줘야 한다!!
  • 다대다 매핑 : 단방향, 양방향 가능!

다대다 매핑의 한계

  • 편리해보이지만 실무에서 사용하지 않는다!!
  • 연결 테이블이 단순히 연결만 하고 끝나는 경우 없다.
  • 주문시간, 수량 같은 추가 데이터가 들어올 수 있음
  • 중간 테이블이 숨겨져있기 때문에, 쿼리도 예상하지 못하게 나간다.

한계 극복

  • 연결 테이블용 엔티티 추가 (연결테이블을 엔티티로 승격)
  • @ManyToMany ➡ @OneToMany, @ManyToOne

참고

@JoinColumn

  • 외래키 매핑시 사용

  • referencedColumnName의 경우 JoinColumn할 때 외래키가 참조하는 대상테이블의 컬럼명(대상 테이블의 PK)가 외래키 명과 다를 수 있다. 그런 경우 적어준다.

@ManyToOne

  • 다대일 관계 매핑

@OneToMany

  • 일대다 연관관계 매핑

잘 보면 @ManyToOne에 없던 mappedBy가 있다.
그말은 즉슨 다대일을 쓰면 해당 엔티티는 무조건 연관관계의 주인이 되어야한다는 뜻


해당 게시글은 인프런 김영한님의 <자바 ORM 표준 JPA 프로그래밍 - 기본편>을 듣고 정리한 내용입니다.

profile
백엔드 꿈나무 🐥

0개의 댓글