Per ardua ad astra !
I'm On My Way
Per ardua ad astra !
전체 방문자
오늘
어제
  • 분류 전체보기 (126)
    • Algorithm (50)
      • 백준 (30)
      • SWEA (3)
      • JUNGOL (3)
      • Programmers (5)
      • LeetCode (2)
    • 안드로이드 개발 (6)
      • Java로 개발 (0)
      • Kotlin으로 개발 (3)
    • Spring (41)
      • Spring기본 (17)
      • JPA기본 (15)
      • JPA활용 SpringBoot 기본 (9)
      • API 개발 기본 (0)
    • 네트워크 (3)
    • 운영체제 (0)
    • Life (3)
      • 책 (0)
      • 자기계발 (1)
      • 일상 (2)
    • 노마드코더 (3)
      • python으로 웹 스크래퍼 만들기 (3)
    • 프로그래밍 언어 (17)
      • Java 기본 (2)
      • 코틀린 기본 (15)

블로그 메뉴

  • 홈
  • 방명록

인기 글

hELLO · Designed By 정상우.
Per ardua ad astra !

I'm On My Way

연관관계 매핑 기초(단방향, 양방향), 다양한 연결관계 매핑
Spring/JPA기본

연관관계 매핑 기초(단방향, 양방향), 다양한 연결관계 매핑

2021. 7. 13. 14:25

객체 지향은 참조에 의한 객체 조회 (객체로 조회 하고싶음) vs RDB는 Join에 의해 객체 조회(외부키로 id를 조회)

둘사이의 괴리를 어떻게 해결할 것인가?? 
=> 객체지향언어에서 관계지향 언어의 개념을 삽입한다.


1. 다대일(N:1) 매핑

회원과 팀이 있다. 한 팀에 여러명의 멤버가 속해 있기 때문에 팀과 회원은 다대일 관계이다. 

(1) Member(회원) 기준(관계에서 N을 맡음)
-> Member의 column에 TeamId가 아닌 Team을 추가 
-> @ManyToOne 추가
-> @JoinColumn(name = "TEAM_ID") 추가

@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;

Member가 Many, Team이 One으로 이해하자


"TEAM_ID"는 외부키로 참조할 다른 테이블의 기본키 속성명이다 ??

아니다 !!  JoinColumn을 이용하면 외부키 참조를 알아서 하는 것이고, 가져온 참조값을 DB Column에 어떤 이름으로 저장할까? 에대한 내용을 담는것이 "name" 속성이다. 헷갈리지 말자 !!

어노테이션이 붙은 변수 Team은 객체지향의 의도대로 RDB의 참조 방식의 id참조가 아닌 객체를 직접 참조한다.(DB 상에선 id 참조로 표시됨) JoinColumn 어노테이션을 이용하여 외부키 참조를 쉽게 하여 RDB의 의도 또한 만족할 수 있다.


이미 이것을 끝낸 것만으로도 단방향 연관관계를 설정한 것이다. RDB에서는 외래키 참조를 통해서 Member가 어느 팀에 속해 있는 가를 조회가 가능하지만, 우리는 Member들의 팀필드만 설정했기 때문에 Team에서 Member를 조회할 순 없었다. TEAM에서 Member를 조회하고 싶을 땐 class Team의 필드에 member list를 만들어서 설정해주고 이 리스트에도 어노테이션을 붙여 양방향 연관관계를 설정해주어야 한다. 

 

(2) Team(팀) 기준(관계에서 1을 맡음)

@OneToMany(mappedBy = "team")
List<Member> members = new ArrayList<Member>();

 

Class Team은 N:1의 관계에서 1의 역할이므로 OneToMany 어노테이션을 붙이고 mappedBy 속성에 Member Class에서 설정한 Team 객체의 필드명으로 값을 설정해 주어야 한다. 

양방향 매핑은 사실 객체지향 언어에서 단방향 매핑 2개를 서로 교차에서 만들어주는 것이다. (RDB를 흉내내는 정도로) 양방향은 실제 실무에서 많이 쓰이기도 하지만 쓸 때는 항상 주의해야하고 실제론 단방향 매핑을 잘하는 것이 더 중요하다고 말씀하셨다.

 

 

2. 일대다 

-  일(1)이 연관관계의 주인인 경우를 일컫는데, 자주 사용하진 않는다.
    ex) 팀과 멤버 테이블이 있고 1:N의 관계인데, 팀이 주인이되는 경우. (멤버 쪽에서 팀 객체를 다루지 않음) 

- 테이블 일대다 관계는 DB설계상 항상 다(N)쪽에 외래키가 있다. 객체와 테이블의 차이 때문에 반대편 테이블의 외래키를 관리하는 특이한 구조가 된다. 

- 일대다 방식은 보통 잘 사용하지 않는다. 일이 연관관계의 주인이고 더 자주 사용하더라도, 차라리 다대일 방식으로 만들고 양방향 관계를 둬서 조회하는것이 성능적으로도, 전략적으로도 더 편하고 알기 쉽다.

단점
- 엔티티가 관리하는 외래키가 다른 테이블에 있다.
- 연관관계 관리를 위해 추가로 update sql 실행

3. 일대일

1. 주테이블에 외래키를 두는 경우 .
-> 주테이블 설정은 관점에 따라 다름
-> 주 객체가 대상 객체의 참조를 가지는 것처럼 주 테이블에 외래 키를 두고 대상 테이블을 찾음
-> 객체지향 개발자 선호 
-> JPA 매핑 편리


장점: 주 테이블만 조회해도 대상 테이블에 데이터가 있는지 확인 가능
단점: 값이 없으면 외래키에 null 허용

2. 대상 테이블에 외래키 를 두는 경우
대상 테이블에 외래키가 존재
-> 전통적인 데이터베이스 개발자 선호
장점: 주 테이블과 대상 테이블을 일대일에서 일대다로 변경할때 테이블 구조 유지
단점: 프록시 기능의 한계로 지연 로딩으로 설정해도 항상 즉시 로딩됨


사용방법 간단예시

ex) 만약에 Order(주 테이블)와 Delivery(대상 테이블)가 1:1로 매핑되는데, Delivery의 id값을 Order에서 외래키로 가지고 있다면, Order가 이 관계의 주인이 되므로 주테이블에 외래키를 둔다면, 예시 코드는 다음과 같다

 

Class Order

@Entitiy
public class Order{

	@OneToOne
	@JoinColumn(name = "DELIVERY_ID")
	private Delivery delivery;
	
    // ...생략
}

 

으로 관계를 설정한다. 추가적으로 양방향 매핑을 하고 싶다면

 

Class Delivery

@Entity
public class Delivery{

	@OneToOne(mappedBy = "delivery")
   	 private Order order;
}

mappedBy 속성을 이용해서 추가하면 된다.


4. 다대다 

-> 편리해보이지만 실무에선 사용불가하다.
-> 연결 테이블이 단순히 연결만 하고 끝나지 않고, 연결테이블에도 주문시간, 수량과 같은 여러 데이터가 들어 올 수 있다.

한계 극복 
연결 테이블용 엔티티 추가 (연결 테이블을 엔티티로 승격)
@ManyToMany -> @ManyToOne, @OneToMany로 변경 

ex) 만약 Item과 Category가 N:M 연관관계를 가지는 경우, 중간의 연결테이블을 엔티티로 승격시켜서 하나 만들어준다. 

중간 테이블 ItemCategory를 만들면 

Item과 ItemCategory의 관계는 1:N, Category와 ItemCategory의 관계는 1:M이 된다. 설정해보자

 

Class Item

@Entity
public class Item {
    @Id @GeneratedValue
    @Column(name = "ITEM_ID")
    private Long id;
    
    @OneToMany(mappedBy = "item")
    private List<ItemCategory> categories = new ArrayList<>();
    
    //...생략
}

 

관계에서 1을 맡고 있음으로 @OneToMany와 mappedBy 속성을 사용하여 양방향 연결시킨다.

아래의 Category도 마찬가지다.

 

Class Category

@Entity
public class Category {
    @Id @GeneratedValue
    @Column(name = "Category_ID")
    private Long id;
    
    @OneToMany(mappedBy = "category")
    private List<ItemCategory> items = new ArrayList<>();
    
    //...생략
}

 

Class ItemCategory

@Entity
public class ItemCategory {

    @Id @GeneratedValue
    private Long id;

    @ManyToOne
    @JoinColumn(name = "ITEM_ID")
    private Item item;

    @ManyToOne
    @JoinColumn(name = "CATEGORY_ID")
    private Category category;

    public void addItemCategory(){
        this.item.getCategories().add(this);
        this.category.getItems().add(this);
    }
    
}

Item과 Category에 각각 @ManyToOne 어노테이션을 이용하여 매핑한 모습이다.

addItemCategory()메소드는 Item과 Category의 각각의 리스트에 이 ItemCategory를 추가하는 임의로 생성한 메소드 이다.

 

실행화면

/

임의로 데이터를 넣고 Item, Category, ItemCategory를 조회했을 때 모습이다.  ITEMCATEGORY에서 우유에 매핑된 Category가 2개 인것으로 확인할 수 있다. 

 

 

5. 부모 객체와 자식객체를 가진경우

 

만약 카테고리가 부모 카테고리가 있고, 자식카테고리가 있을 수도 있다. 

그런 경우는 부모 ID를 외부키로 사용하면 되고 양방향을 원할경우 자식 카테고리 리스트를 만들어서 mapped할 수도있다. 

 

@Entity
public class Category {
    @ManyToOne
    @JoinColumn(name = "PARENT_ID")
    private Category parent;

    @OneToMany(mappedBy = "parent")
    private List<Category> child = new ArrayList<>();

    // ...생략
}

 

이 게시물은 '자바 ORM 표준 JPA 프로그래밍' 강의를 수강하고 정리한 내용임을 밝힙니다. 
출처: https://www.inflearn.com/course/ORM-JPA-Basic#

 

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의

JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., 본 강의는 자바 백엔

www.inflearn.com

 

    'Spring/JPA기본' 카테고리의 다른 글
    • JPA 프록시 활용, 즉시로딩 vs 지연로딩(fetch = FetchType.LAZY)
    • 상속 관계 매핑 (Inheritance), 정보 상속(MappedSuperclass)
    • 자동 DDL 쿼리생성, 엔티티 매핑을 위한 기본키, 콜롬 어노테이션
    • JPA 시작
    Per ardua ad astra !
    Per ardua ad astra !
    개발자 지망생이며 열심히 공부하고 기억하기 위한 블로그입니다.

    티스토리툴바