JPA 연관관계

김예진·2022년 12월 7일
0

java

목록 보기
13/21

✨연관관계 매핑이란?

-객체의 참조와 테이블의 외래 키를 매핑하는 것을 말함
-연관관계는 방향, 연관 관계의 주인, 다중성을 고려해야 함

✨단방향, 양방향

-데이터베이스 테이블은 외래 키 하나로 양 쪽 테이블 조인이 가능함 따라서 데이터베이스는 단방향이니 양방향이니 나눌 필요가 없음
-하지만 객체는 참조용 필드가 있는 객체만 다른 객체를 참조하는 것이 가능 그렇기 때문에 두 객체 사이에 하나의 객체만 참조용 필드를 갖고 참조하면 단방향 관계, 두 객체 모두가 각각 참조용 필드를 갖고 참조하면 양방향 관계라고 함
(엄밀히 말하자면 양방향 관계라는 것은 없고, 두 객체가 단방향 참조를 각각 가져서 양방향 관계처럼 사용하고 말하는 것)

단방향 관계 (A -> B)

class A{
	B b;
}
class B {

}

양방향 관계 (A <-> B)

class A {
	B b;
}
class B {
	A a;
}

-jpa를 사용하여 데이터베이스와 패러다임을 맞추기 위해서 객체는 단방향 연관 관계를 가질지, 양방향 연관 관계를 가질지 선택해야 함 선택은 비즈니스 로직에서 두 객체가 참조가 필요한지 여부를 고민해보면 됨

❓무조건 양방향 관계를 하면 쉽지 않나

-객체 입장에서 양방향 매핑을 했을 때 오히려 복잡해질 수 있음
-예를 들어 사용자 엔티티는 굉장히 많은 엔티티와 연관관계를 가짐
이런 경우 모든 엔티티를 양방향 관계로 설정하게 되면 사용자엔티티는 엄청나게 많은 테이블과 연관 관계를 맺게 되고
user클래스도 매우 복잡해지게 됨
그리고 다른 엔티티들도 불필요한 연관관계 매핑으로 인해 복잡성이 증가할 수 있음
그래서 양방향으로 할지 단방향으로 할지 필히 구분해줘야 함
구분하기 좋은 기준은 기본적으로 단방향 매핑으로 하고 나중에 역방향으로 객체 탐색이 꼭 필요하다고 느낄 때 추가하는 것으로 잡으면 됨

두 객체가 양방향 관계(단방향 관계 2개)를 맺을 때, 연관 관계의 주인을 지정해야 함

✨연관관계의 주인

-연관 관계의 주인을 지정하는 것은 두 단방향 관계 중, 제어의 권한(외래 키를 비롯한 테이블 레코드를 저장, 수정, 삭제, 처리)을 갖는 실질적인 관계가 어떤 것인지 JPA에게 알려준다고 생각하면 됨
-연관 관계의 주인은 연관 관계를 갖는 두 객체 사이에서 조회, 저장, 수정, 삭제를 할 수 있지만, 연관 관계의 주인이 아니면 조회만 가능
-연관 관계의 주인이 아닌 객체에서 mappedBy 속성을 사용해서 주인을 지정해줘야 함
-tip: 외래 키가 있는 곳을 연관 관계의 주인으로 정하면 됨

❓왜 연관 관계의 주인을 지정해야할까?

예를 들어 Board와 Post 객체가 있고 양방향 연관 관계를 갖는다고 생각해보자
게시글(Post)의 게시판을 다른 게시판(Board)으로 수정하려 할 때,
Post객체에서 'setBoard(...)' 같은 메소드를 이용해서 수정하는게 맞는지, Board객체에서 'getPosts()' 같은 메소드를 이용해서 List의 게시글을 수정하는게 맞는지 헷갈릴 수 있다
두 객체 입장에서는 두 방법 다 맞는 방법이긴 하지만, 이렇게 객체에서 양방향 연관 관계 관리 포인트가 두 개일 때는 테이블과 매핑을 담당하는 JPA입장에서 혼란을 주게 된다
그렇기 때문에 두 객체 사이의 연관 관계의 주인을 정해서 명확하게 Post에서 Board를 수정할 때만 FK를 수정하겠다고 정하는 것이다

❓연관 관계의 주인만 제어하면 되나?

-데이터베이스에 외래 키가 있는 테이블을 수정하려면 연관 관계의 주인만 변경하는 것이 맞을까?
-맞긴 하지만 이건 데이터베이스만 생각했을 때이다
-객체를 생각해보면 사실 둘 다 변경해주는 것이 좋다(연관 관계의 주인이 아닌 곳에서도 변경)
-왜냐하면 두 참조를 사용하는 순수한 두 객체는 데이터 동기화를 해줘야하기 때문이다

✨다중성

-데이터베이스를 기준으로 다중성을 결정함
-연관 관계는 대칭성을 가진다
(
일대다 <-> 다대일
일대일 <-> 일대일
다대다 <-> 다대다
)

🫧다대일(N:1)

-게시판(Board)과 게시글(Post)의 관계로 예시를 들겠다

요구사항

  • 하나의 게시판에는 여러 게시글을 작성할 수 있다
  • 하나의 게시글은 하나의 게시판에만 작성할 수 있다
  • 게시글과 게시판은 다대일 관계를 갖는다

데이터베이스를 기준으로 다중성을 게시글N : 게시판1로 결정했다
즉 외래 키를 게시글(N)이 관리하는 일반적인 형태이다(참고로 데이터베이스는 무조건 다쪽이 외래 키를 갖는다)

다대일(N:1) 단방향

@Entity
public class Post {
    @Id @GeneratedValue
    @Column(name = "POST_ID")
    private Long id;

    @Column(name = "TITLE")
    private String title;

    @ManyToOne
    @JoinColumn(name = "BOARD_ID")
    private Board board;
    //... getter, setter
}

@Entity
public class Board {
    @Id @GeneratedValue
    private Long id;
    private String title;
    //... getter, setter
}

다대일 단방향에서는 다 쪽인 Post에서 @ManyToOne만 추가해준 것을 확인할 수 있다
반대로 Board에서는 참조하지 않는다(단방향이기 때문)

다대일(N:1, 1:N) 양방향

@Entity
public class Post {
    @Id @GeneratedValue
    @Column(name = "POST_ID")
    private Long id;

    @Column(name = "TITLE")
    private String title;

    @ManyToOne
    @JoinColumn(name = "BOARD_ID")
    private Board board;
    //... getter, setter
}

@Entity
public class Board {
    @Id @GeneratedValue
    private Long id;
    private String title;

    @OneToMany(mappedBy = "board")
    private List<Post> posts = new ArrayList<>();
    //... getter, setter
}

다대일 양방향으로 만드려면 일(Board) 쪽에 @OneToMany를 추가하고,
양방향 매핑을 사용했으니 연관관계의 주인을 mappedBy로 지정해준다
mappedBy로 지정할 때 값은 대상이 되는 변수명을 따라 지정하면 된다. 여기서 Post객체(대상)의 board라는 이름의 변수이기 때문에 board로 지정했다

참고: mappedBy 사용법
-연관관계의 주인은 mappedBy 옵션을 사용하지 않고 반대쪽 객체에서 사용한다

🫧일대다(1:N)

일대다(1:N) 단방향
(참고로 실무에서는 일대다 단방향은 거의 쓰지 않도록 한다고 함)
-데이터베이스 입장에서는 무조건 다(N)쪽에서 외래키를 관리함 근데 일(Board)에서 다(Post)쪽 객체를 조작(생성,수정,삭제)하는 방법
-다대일과 비슷해보이지만 다대일에서는 연관관계의 주인이 다(N)쪽이고, 일대다에서는 연관관계의 주인이 일(1)쪽임

@Entity
public class Post {
    @Id @GeneratedValue
    @Column(name = "POST_ID")
    private Long id;

    @Column(name = "TITLE")
    private String title;
  //... getter, setter
}

@Entity
public class Board {
    @Id @GeneratedValue
    private Long id;
    private String title;

    @OneToMany
    @JoinColumn(name = "POST_ID") //일대다 단방향을 @JoinColumn필수
    private List<Post> posts = new ArrayList<>();
    //... getter, setter
}

-본인 테이블에 외래키가 있다면 저장 및 처리를 INSERT 한번으로 처리가 가능하지만, 다른 테이블에 있다면 UPDATE문을 추가 실행해야한다는 단점이 있음

🫧일대일(1:1)

일대일 양방향

@Entity
public class Post {
    @Id @GeneratedValue
    @Column(name = "POST_ID")
    private Long id;

    @Column(name = "TITLE")
    private String title;
    @OneToOne
    @JoinColumn(name = "ATTACH_ID")
    private Attach attach;
    //... getter,setter
}
@Entity
public class Attach {
    @Id @GeneratedValue
    @Column(name = "ATTACH_ID")
    private Long id;
    private String name;

    @OneToOne(mappedBy = "attach")
    private Post post;
  //... getter, setter
}

참고자료
https://jeong-pro.tistory.com/231

0개의 댓글