JPA - 연관관계 매핑 2

Sungjin·2021년 7월 9일
1

JPA

목록 보기
6/10
post-thumbnail

공부할 내용!!

연관 관계 매핑 시, 연관관계의 주인을 객체 관계에서 어느 방향에 주어지느냐에 따라 어떻게 달라지는 지 알아봅시다 :)

  • 연관관계 매핑시 중요내용 3가지
    1. 다중성
      연관관계가 1:N, N:1, N:M, 1:1 로 나뉘어질 수 있음
    2. 단방향, 양방향
    • 테이블
      1. 외래 키 하나로 양쪽 조인 가능
      2. 방향 개념이 없다 봐도 무방
    • 객체
      1. 참조용 필드가 있는 쪽으로만 참조 가능
      2. 한 쪽만 참조하면 단방향, 양쪽이 서로 참조하면 양방향
    1. 연관관계의 주인
    2. 테이블은 외래 키 하나로 두 테이블이 연관관계를 맺음
    3. 객체 양방향 관계는 참조가 2군데. 즉, 객체 사이의 외래 키를 관리할 곳을 지정
    4. 연관관계의 주인: 외래 키를 관리하는 참조
    5. 매핑된 객체: 단순 조회만 가능. 외래 키에 영향 x

공부해 볼 테이블 (다대일, 일대다)

다대일 N:1

연관관계의 주인이 N쪽에 있는 형태. 실무에서 가장 많이 쓰는 일반적인 형태라고 볼 수 있습니다.

연관관계 매핑 1편
이곳에 정리 해두었어요~

일대다 1:N

연관관계의 주인이 1쪽에 있는 형태.

일대다 단방향

코드 부터 보시죠

Member Entity

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

 } 

Tema Entity

@Entity
 public class Team {
 
 @Id @GeneratedValue
 @Column(name="team_id")
 private Long id;
 
 private String name; 
 
 @OneToMany
 @JoinColumn(name="member_id")
 private List<Member> members= new ArrayList<Member>();

 }

코드에서 확인해 볼 수 있으듯이, Member Entity에는 자기 자신외엔 아무 연관관계의 정보가 없습니다.
Team Entity에는 일대다 관계로서 member_id 를 매핑하고 있습니다.
하지만! 문제가 있습니다. 외래 키의 주인은 DB설계상 Member 테이블입니다.

외래키의 주인이 Member테이블인 이유??
연관관계 매핑 1편
확인 해주세요~

외래키의 주인이 Member 테이블인 것이 왜 문제?? 객체를 persist하는 코드를 먼저 보시죠


//회원 저장
Member member-new Member();
...

em.persist(member);

// 팀 저장
Team team=new Team();
...
team.getMembers().add(member); //이 부분이 문제!
em.persist(team);

team.getMembers().add(member); 이 부분이 문제가 됩니다.
분명 외래 키의 주인은 Member 테이블입니다.
즉, 연관관계의 주인인 Team 객체는 반대편 테이블인 Member 테이블의 외래 키를 관리하게 되는 상황이죠.
위의 이유로 Team 객체가 INSERT문이 DB에서 나갈 때 마다 변경에 따라 Member 테이블의 외래 키도 수정되어야 하기 때문에 UPDATE문이 DB에 추가로 나가게 됩니다.
이는 성능의 문제를 일으킬 수 있습니다!!

  • 정리 : 일대다 단방향 매핑보다는 다대일 양방향 매핑을 사용합시다!

일대다 양방향

Member Entity

@Entity
 public class Member { 
 
 @Id @GeneratedValue
 @Column(name="member_id")
 private Long id;
 
 private String name;
 
 @ManyToOne
 @JoinColum((insertable=false, updatable=false)
 private Team team

 } 

Member Entity의 코드만 이런 식으로 바꿔주면 됩니다.

  • 이런 매핑은 JPA 스펙상 제공하지 않는 행위라 합니다
    • 따라서 @JoinColumn에 속성을 줘서 읽기 전용으로 만들어 줍니다.

      이러한 이유는 연관관계의 주인이 아닌 반대편 객체인 Member 에서 또 객체를 참조하는 것이기 때문입니다.
      즉, 읽기 전용으로 해 놓지 않으면 DB에 SQL이 나가는 시점에 서로 외래 키를 참조할 것 입니다.

  • 정리 : 불편한 점과 실제로 사용했을 때 얼마나 불편할 지 다 보여준 것 같습니다. 다대일 연관관계를 되도록이면 사용합시다!

공부해 볼 테이블 (일대일)

일대일 1:1

  • 주 테이블이나 대상 테이블 중에 외래 키 선택 가능합니다. 그러니, 비즈니스 로직이 많이 쓰일 곳에 외래키를 놓읍시다!
  • 외래 키에 데이터베이스 유니크 제약 조건을 걸어야 합니다.
    • 일대일 관계이므로 그래요 :)
  • 외래 키가 있는 곳이 연관관계의 주인!

Member Entity

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

 @OneToOne
 @JoinColumn(name="locker_id")
 private Locker locker; //객체를 직접 참조
 } 

Locker Entity

@Entity
 public class Locker { 
 
 @Id @GeneratedValue
 @Column(name="locker_id")
 private Long id;
 
 private int location;

 @OneToOne(mappedBy="locker")
 private Member member; //객체를 직접 참조
 } 
  • 정리 : 연관관계의 주인을 외래 키의 주인 방향으로 설정!
    • 대상 테이블에 외래 키를 단방향으로 매핑하는 것은 JPA에서 지원하지 않는다고 합니다!
    • 주 테이블에 외래키
      1. 주 테이블에 외래 키를 두고 대상 테이블을 찾음. -> JPA 매핑 편리
      2. 주 테이블만 조회에도 대상 테이블에 데이터 있는지 확인 가능 (주 테이블이 왜래 키를 갖고 있기 때문!)
    • 대상 테이블에 외래 키
      1. 프록시 기능의 한계로 지연 로딩으로 설정해도 항상 즉시 로딩됨.

        대상 테이블에 외래 키를 갖고 있기 때문에 주 객체인 Member 객체를 조회해도 대상 객체인 Locker 객체를 조회하지 못합니다.
        따라서 DB에 주 테이블인 Member테이블의 PK값을 가지고 대상 테이블인 Locker 테이블의 외래 키와 같은 지 확인 해 보는 작업이 추가로 필요하게 됩니다.

다대다 N:M

  • 관계형 DB는 테이블 2개로 다대다 관계를 표현할 수 없습니다.
  • 테이블을 추가 해서 일대다, 다대일 관계로 연결하여 줘야합니다.
  • @ManyToMany와 @JoinTable을 활용하면 객체사이의 다대다 관계를 만들 수는 있지만, 실무에서는 사용하지 않는다고 합니다.
    • 이렇게 만들어진 중간 테이블은 매핑정보만 포함하고 있지 주문은 언제 했고, 얼마나 주문 했고 이런 데이터가 들어오지를 못하기 때문에 사용하기 어렵습니다.

즉, 이와 같은 코드를 짭시다!

Order Entity


@Entity
@Table(name="orders")
public class Order{

	@Id @GeneratedValue
    @Column(name="order_id")
    private Long id;
    
    @oneToMany(mappedBy="order")
    private List<OrderItem> orderItems=new ArrayList<OrderItem>();
    
    private LocalDateTime orderDate;
}

Item Entity


@Entity
public class Item{

	@Id @GeneratedValue
    @Column(name="item_id")
    private Long id;
    
    @oneToMany(mappedBy="item")
    private List<OrderItem> orderItems=new ArrayList<OrderItem>();
    
    private String name;
    private int price;
}

OrderItem Entity


@Entity
public class OrderItem{

	@Id @GeneratedValue
    @Column(name="orderitem_id")
    private Long id;
    
    @ManyToOne
    @JoinColumn(name="order_id")
    private Order order;
    
    @ManyToOne
    @JoinColumn(name="item_id")
    private Item item;
    
    private int orderPirce;
    private int quantity;
}

이런 식으로 연결 테이블을 놓으면 되게 됩니다!

  • 정리 : @ManyToMany 가 아닌 연결 테이블용 엔티티를 추가하자!

이상으로 블로그 포스팅을 마치도록 하겠습니다. 감사합니다 :)

이 글은 인프런 김영한님의 '자바 ORM 표준 JPA 프로그래밍 - 기본편'을 수강하고 작성합니다.
출처:https://www.inflearn.com/course/ORM-JPA-Basic

profile
WEB STUDY & etc.. HELLO!

1개의 댓글

comment-user-thumbnail
2021년 7월 11일

잘 보고 갑니다 ~~!

답글 달기