GrowMe

[JPA] 엔티티 간 연관관계 매핑 방법 본문

About Spring

[JPA] 엔티티 간 연관관계 매핑 방법

오늘도 타는중 2022. 7. 12. 01:41
JPA 엔티티 간 연관관계 매핑 방법
# JPA 연관관계 정의 규칙
# 양방향 VS 단방향
# 연관관계의 주인
#
1 : N
# N : 1
# N : N
# 1 : 1

🎮JPA 기본 사용법이 궁금하다면 -> https://grow-myself.tistory.com/33


*JPA에서 가장 중요한 것

  • 객체와 테이블이 어떻게 매핑되는지 이해하는 것이 JPA에서 가장 중요
  • 왜냐하면 그것이, JPA의 목적인 객체 지향 프로그래밍과 데이터베이스 사이의 패러다임 불일치를
  • 해결하는 열쇠이기 때문
  • 특히, 객체와 테이블 간 매핑을 넘어 엔티티 간의 연관관계 매핑의 이해는 어려우므로 더욱 중요하다.

*연관 관계 정의 규칙

  • 다중성 : 다대일(N:1), 일대다(1:N), 일대일(1:1), 다대다(N:N)
  • 방향 : 단방향, 양방향 (객체 참조)
  • 연관 관계의 주인 : 양방향일 시, 연관 관계를 관리하는 주체

* 단방향 연관 관계

  • Member 클래스만 Order 객체를 참조할 수 있다. (Member 클래스 내, List<Order>를 통해 접근 가능)

  • Order 클래스만 Member 클래스를 참조할 수 있다. (Order 클래스 내, Member 객체 존재)

-> 이처럼, 한쪽 클래스만 다른 쪽 클래스가 참조 가능한 관계 : 단방향 연관 관계


* 양방향 연관 관계

  • Member : List<Order>를 통해 Order 객체 참조 가능
  • Order : 선언된 Member 객체를 통해, Member 객체 참조 가능

-> 양쪽 모두 참조 가능한 관계 : 양방향 연관 관계


* 단방향 VS 양방향 선택 기준

  • Member.getOrder() 참조가 필요 시, Member -> Order 단방향 참조
  • Order.getMember() 참조가 필요 시, Order -> Member 단방향 참조
  • 둘 다 필요하다? : 양방향 참조

*연관 관계의 주인

  • 두 객체 (A, B)가 양방향 관계이면, 둘 중 관리 주체인 주인 객체를 지정해줘야 JPA가 혼동하지 않는다.
  • 가령, 게시글(Post)의 게시판을 다른 게시판(Board)로 수정하려할 때, Post 객체에서 setBoard()로 수정할 지, Board 객체에서 getPosts() 등의 메서드를 통해 수정할 지, JPA가 헷갈릴 수 있다. 따라서 관리 주체를 정해
  • JPA에게 알려주어야 한다.

*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;
}

@Entity
public class Board {
    @Id @GeneratedValue
    private Long id;
    private String title;
}
  • @ManyToOne : 다대일의 관계를 명시(N 쪽의 클래스에, 상대쪽 객체를 참조 가능한 필드에 선언한다)
  • @JoinColumn(name = ) : 생성되는 외래키의 이름을 설정해준다. N:1 연관관계에서 @JoinColumn은 생략 가능
  • 외래키 칼럼은 상대 쪽의 PK(기본키)로 설정되어 생성된다.
  • (이는 @JoinColumn의 referencedColumnName 옵션을 통해 기본키가 아닌 컬럼으로 수정이 가능하며, 관련된 엔티티들에 Serializabled을 상속하여 구현해주어야 한다.)

*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;
}


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

    @OneToMany(mappedBy = "board")
    List<Post> posts = new ArrayList<>();
}
  • @OneToMany : 양방향으로 만들기 위해, 상대쪽을 참조가능한 필드(List<Post> posts)에 이 어노테이션을 추가.
  • mappedBy 속성 : 연관관계의 주인을 상대 쪽으로 지정한다. 관리의 주체가 상대편인 Post가 되며, 값은 상대 객체의 필드 중 자신을 참조하는 필드로 지정해준다.

*1 : N 단방향

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

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


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

    @OneToMany
    @JoinColumn(name = "POST_ID") //일대다 단방향은 @JoinColumn필수
    List<Post> posts = new ArrayList<>();
}
  • @JoinColumn : 1: N 연관관계에서는 이 어노테이션을 생략하면, 자동으로 두 테이블을 연결해주는 중간 테이블을 생성하기 때문에, 비생산적이므로 꼭추가해줍니다.

*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;
}

@Entity
public class Attach {
    @Id @GeneratedValue
    @Column(name = "ATTACH_ID")
    private Long id;
    private String name;
}
  • 주 테이블(Post)과 대상 테이블(Attach) 모두 외래키를 가질 수 있다.
  • 게시글(Post)에 첨부파일(Attach)을 1개만 첨부할 수 있다고 가정
  • @JoinColumn : Post 테이블에 외래키를 지정하여 Attach와 매핑합니다.

*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;
}


@Entity
public class Attach {
    @Id @GeneratedValue
    @Column(name = "ATTACH_ID")
    private Long id;
    private String name;

    @OneToOne(mappedBy = "attach")
    private Post post;
}
  • Attach 테이블에 @OneToOne 에너테이션을 추가하고, mappedBy로 주인이 Post임을 JPA에게 알려줍니다.
  • 주 테이블에 외래키를 두는 것이 보통이지만, 상황마다 성능 차가 생길 수 있어, 대상 테이블에 외래키를 두는 경우도 있습니다.

*N : N 연관관계

  • 중간 테이블을 하나 추가하여, 두 개의 다대일 관계를 만들어 준다.
  • 중간 테이블은 각 테이블의 외래키를 가지고 있다.
  • 각 다대일 관계는 :
  • 다대일 매핑을 먼저 한 후, 참조가 추가로 필요하면, 일대다 매핑을 추가하여, 양방향으로 만들어준다.
@Getter
@Entity
@NoArgsConstructor
public class OrderCoffes {
    @ManyToOne
    @JoinColumn(name = "COFFEE_ID")
    private Coffee coffee;
    @ManyToOne
    @JoinColumn(name = "ORDER_ID")
    private Order order;

    @Column(nullable = false)
    private int quantity;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long orderCoffesId;

    public void addCoffee(Coffee coffee) {this.coffee = coffee;}
    public void addOrder(Order order) {this.order = order;}
}

@Getter
@Setter
@Entity(name = "ORDERS")
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long orderId;

    @Enumerated(EnumType.ORDINAL)
    private OrderStatus orderStatus = OrderStatus.ORDER_REQUEST;

    @Column(nullable = false)
    private LocalDateTime createdAt = LocalDateTime.now();

    @Column(nullable = false, name = "LAST_MODIFIED_AT")
    private LocalDateTime modifiedAt = LocalDateTime.now();

    @OneToMany(mappedBy = "order", cascade = CascadeType.PERSIST)
    private List<OrderCoffes> orderCoffes = new ArrayList<>();
 }
 
 @NoArgsConstructor
@Getter
@Setter
@Entity
public class Coffee {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long coffeeId;

    @Column(length = 100, nullable = false)
    private String korName;

    @Column(length = 100, nullable = false)
    private String engName;

    @Column(length = 5, nullable = false)
    private int price;

    @Column(length = 3, nullable = false, unique = true)
    private String coffeeCode;

    @OneToMany(mappedBy = "coffee")
    private List<OrderCoffes> orderCoffes = new ArrayList<>();
 }
  • OrderCoffes : N : N 매핑을 위해 추가한 중간 테이블. Order 테이블과, Coffee 테이블의 외래키를 가지고 있다.
  • mappedBy : OrderCoffes 객체가 Order와 Coffee 클래스의 연관관계 주인임을 나타내고 있다.
  • cascade = CascadeType.PERSIST : 객체가 영속화될 때, 연관된 객체도 함께 영속화 시킨다. 여러가지 옵션이 있지만, 스압이 걱정되므로... cascade에 대한 자세한 설명은 추후에 포스팅하도록 하겠다...
Comments