우아한테크코스/레벨3

JPA 기초: 연관관계매핑

nauni 2021. 7. 8. 08:44

수업

  • A객체 → B 객체 참조 : 이동가능
  • B 객체 → A 객체 참조가 없다면 : 이동불가
  • 하지만 테이블은 join으로 이동가능 (항상 양방향)
  • 연관관계의 주인: 객체와 객체 사이의 관계 (실제 DB와 매핑하면서 주인을 정해야 함 → 주인 대신 외래키 관리자 라고 해보자!)

연관관계: 다대일, 일대다, 양방향, 일대일 

다대일 단방향

  • ManyToOne : 다대일
  • JoinColumn: 해도되고 안해도됨(foreign key의 이름을 지정해주는 역할) → 생략된다면 엔티티이름+id로 지정됨
  • 단방향관계
    • 엔티티
    • Station -> Line: 가능
    • Line -> Station: 불가능
    • 테이블
    • station -> line: 가능
    • line -> station: 가능
  • 지하철에 Line 정보추가 → 지하철입장에서는 다대일 단방향 관계 (Station→Line 가능, Line→Station 불가능)
  • 👉 jpa 연관된 모든 엔티티는 영속상태여야함
  • 쿼리 지식없이 JPA 사용은 불가하지!!
  • 연관관계에서 지우려면 기존 연관관계를 먼저 제거하고 제거해야함!
  • 양방향이 의미하는것? 객체에서!!
  • OneToMany? mappedBy → 누가 fk를 들고 있지?
  • Station의 어느 필드가 fk 를 가지고 있지??

@Entity
public class Line {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(nullable = false)
    private String name;

    @OneToMany(mappedBy = "line") // Station의 line 이라는 필드가 키를 가짐
    private List<Station> stations = new ArrayList<>();
}
@Entity
@Table(name = "station")
public class Station {
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    // ...
    @ManyToOne
    @JoinColumn(name = "line_id", nullable = true)
    private Line line;
}
  • mappedBy가 없다면 line이 키를 관리하려고 할것임 그럼 List<Station>이 10이면 Line 테이블에는 StationId가 다른 10개의 row가 생길것!!
  • List<Station>: OneToMany에 mappedBy 설정이 없다면?
    → line_station 테이블인 line, station 테이블을 연결하는 연결테이블(line_station)을 생성함!!
  • List<Station>: OneToMany에 mappedBy 설정이 있다면?
    → line, station 테이블2개로만 관리!
  • mappedBy는 연결테이블 없이 관리할 수 있다고 힌트를 주는 것!!

양방향 연관관계

  • target에서도 OneToOne 설정? 그럼 mappedBy로 되어있다고 알려주는것?!
  • 연관관계가 어디의 필드인지 mappedBy로 알려줌

연관관계의 주인

  • 각각의 입장에서는 단방향임!!!
  • 객체의 입장에서
    • OneToMany 단방향
    • ManyToOne 단방향
  • 하지만 둘은 개발자 입장에서 양방향으로 알 수 있는거지(단방향 *2)
  • 키를 가지고 있는 사람 → 연관관계의 주인!
  • 연관관계의 주인이라는 말보단 외래키 관리자가 와닿는다!!!!
  • 외래키 관리자를 통해서만 외래키 등록, 삭제등등 이 가능
  • 외래키 관리자가 아닌 것(= Line) → 읽기만 가능 (mappedBy 설정)
  • 👉 보통 (1:N, N:1 상황에서) 다 쪽이 외래키를 가짐
  • 👉 연관관계의 주인을 정하는 것은 키 관리자를 선택하는 것! (비지니스 중요도로 접근 하지 말자!)
  • 👉 대게 다 쪽이 외래키를 가짐 = 관리자 = 주인
  • 양방향 연관 관계는 결국 양쪽 다 신경 써야 한다. (조회하는 쪽 + 외래키 관리자)
station.setLine(line);
line.addStation(station);
  • 한번에 양방향 관계를 설정하는 메소드를 → 연관관계 편의 메소드라고 한다.
  • ⏰ 양방향 매핑 시에는 무한 루프에 빠지지 않게 조심해야 한다.
  • 편한것은 리스크가 존재!
  • JPA가 객체를 객체스럽게 사용하기 위함인것!!

일대다 단방향

@Entity
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @OneToMany
    @JoinColumn(name = "member_id") // 직접 이름을 지정해준다면? (여기서 name은 연관관계가 어떤 어노테이션인지에 따라 동작방식이 다름), 여기서는 fk 추가해줌
    // 여기서는 중간테이블 추가되지 않음(name을 직접 지정해준다면)
    private List<Favorite> favorites = new ArrayList<>();
}
  • 중간테이블 생성을 막으면서 일대다 단방향 관리가 가능
  • 💥 단점이 있음
    → JPA입장에서, 외래키 관리자가 Favorite, 가지고 있니? 갖고는 있는데 잘 모르겠네..?? fk 가지고 있는지 없는지 모르겠네... 부가 쿼리가 나감

일대일 연관관계

class Station {
    Long id;
    String name;
    LineStation lineStation;
}

class LineStation {
    Long id;
}
  • 반대도 일대일 연관관계
  • source, target 하나 정해야함
  • station 주 테이블이면 → line_station 대상테이블이 됨
  • 그냥 하나 주를 정하면 다른쪽이 대상이 됨
  • 사실 외래키가 어디로 가도 큰 상관이 없음.

주테이블에 외래키를 두는 경우는?

  • 단방향 연관관계 생성 → 변화가 있겠지 하나의 엔티티는!
  • OneToOne에서 name이 동작하는 방식? 그 테이블에 FK를 만듦
  • 주테이블 입장에서: 일대일 단방향

대상테이블에 외래키를 두는 경우는?

  • target엔티티에 외래키가 있다면? (1:1 → 1:N 으로 확장이 편함, 테이블 구조가 그대로 가게 됨)
  • 양방향 연관관계만 성립됨(단방향으로는 기술적으로 지원하지 않음)
  • 주 테이블 외래키 → source 엔티티만 조회해도 정보를 알 수 있음

다대다

  • 관계형DB에서는 표현 불가~
  • 그래서 그 사이 연결 테이블이 필요함 (실제 객체 세상에서는 존재하지 않음)
  • 따라서 관계 이외 별다른 정보 추가 불가함
  • 그래서 나중에 확장성을 고려해서라도 1:N, N:1 등으로 설정하고, Section과 같은 별도의 엔티티 객체를 두는 것이 더 좋음
  • 실무에서는 거의 쓸일이 없다
  • 다대다 너무 복잡하니까 가능하면 1:N, N:1 로 풀어서 사용한다!

연관관계 선택

  • 객체지향관점: 일대다 단방향이 깔끔 → 부가 업데이트 쿼리가 무조건 나가게 됨.
  • 기술JPA 관점: 다대일 단방향이 깔끔 → 객체의 입장에서 참조관계가 DB같음.
  • 합의: 다대일 양방향 → 편의 메소드에 대한 고려를 해야함.

추가

  • OneToMany Cascade 옵션 → 영속화하지 않아도 된다
  • CASCADE설정은 처음부터 ALL로 하지말고, 상황에 맞춰서 추가해주는 것이 좋음!
  • 고아객체
    • remove하면 DB에서도 삭제될 것 같은데 관계만 끊어지고 삭제되진 않음
    • orphanRemoval = true → 부모가 없는 자식은 삭제함

질문

  • 엔티티에 equals와 hashcode 관련 메서드 설정해주는 것이 좋을까여?? 네 좋아요
  • 실제 쿼리가 어떻게 나오는지 알 수 있어야 하기 때문에 지금은 쿼리 하나하나 확인해 보는 것이 좋음
  • @DataJpaTest
  • 롬복 → 양방향일때 문제가 됨

참고

  • 우아한테크코스 수업