Spring/JPA

일대일 1:1 / 다대다 N:M

느리지만 꾸준하게 2022. 5. 25. 17:13

일대일 관계

  • 일대일 관계는 반대도 일대일이다.

 

  • 주 테이블이나 대상 테이블 중에 외래 키 선택 가능

 

  • 주 테이블에 왜래 키

 

  • 대상 테이블에 외래 키

 

  • 외래 키에 DB 유니크(UNI) 제약조건 추가

 

다대일(@ManyToOne) 단방향 매핑과 유사하다.

 

 

Locker를 만들어주자.

package hellojpa;

import javax.persistence.GeneratedValue;
import javax.persistence.Id;

public class Locker {

    @Id @GeneratedValue
    private Long id;
    
    private String name;
}

 

Member Class에도 설정을 해주고 돌리면 Locker_ID와 Member Class가 생성이 된다.

@OneToOne
@JoinColumn(name = "LOCKER_ID")
private Locker locker;
Hibernate: 
    
    create table Locker (
       id bigint not null,
        name varchar(255),
        primary key (id)
    )


Hibernate: 
    
    create table Team_Member (
       Team_TEAM_ID bigint not null,
        members_MEMBER_ID bigint not null
    )

 

Locker class에도 걸어주자.

@OneToOne(mappedBy = "locker")
private Member member;

 

 

 

일대일 관계인데 대상 테이블에 외래 키 단방향을 보면

  • 대상 테이블에서의 단방향 관계는 JPA에서 지원하지 않는다.

 

  • 양방향 관계는 지원해줌

 

 

일대일: 대상 테이블에 외래 키 양방향은

  • 일대일 주 테이블에 외래 키 양방향과 매핑 방법은 같다.

MEMBER 테이블에 Locker을 넣고 값이 있으면 돌고 없으면 안돌게 해줄 수 있다. 명확한 1:1 관계이면 이러한 그림이 적절하다.

 

일대일을 정리해보자.

 

주 테이블에 외래 키

  • 주 객체가 대상 객체의 참조를 가지는 것 처럼

 

  • 객체지향 개발자 선호

 

  • JPA 매핑 관리

 

  • 장점: 주 테이블만 조회해도 대상 테이블에 데이터가 있는지 확인이 가능하다.

 

  • 단점: 값이 없으면 외래 키에 null 허용

 

대상 테이블에 외래 키

  • 대상 테이블에 외래 키가 존재

 

  • 전통적인 DB 개발자가 선호

 

  • 장점: 주 테이블과 대상 테이블을 일대일에서 일대다 관계로 변경할 때 테이블 구조 유지

 

  • 단점: 프록시 기능의 한계로 지연 로딩으로 설정해도 항상 즉시 로딩(프록시는 뒤에서 설명)

 

 

이제 다대다 N:M을 보자. 별로 안중요하고 실무에서 쓰면 안되는 방법이다.

 

  • 관계형 DB는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없다.
  • 연결 테이블을 추가해 일대다, 다대일 관계로 풀어내야 한다.

 

객체도 마찬가지로 컬렉션을 사용해서 객체 2개로 다대다 관계가 가능하다.

  • @ManyToMany로 사용가능 하고

 

  • @JoinTable로 연결 테이블 지정

 

  • 다대다 매핑: 단방향, 양방향 가능

 

Product Class를 하나 만들어주자.

package hellojpa;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Product {

    @Id @GeneratedValue
    private Long id;

    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

 

 

Member class에 아래와 같이 넣어준다.

@ManyToMany
@JoinTable(name = "MEMBER_PRODUCT")
private List<Product> products = new ArrayList<>();

 

MEMBER_PRODUCT 테이블이 생기고 MEMBER_PRODUCT 제약조건이 생기게 된다.

create table MEMBER_PRODUCT (
   Member_MEMBER_ID bigint not null,
    products_id bigint not null
)

Hibernate: 

alter table MEMBER_PRODUCT 
   add constraint FKc6hsxwm11n18ahnh5yvbj62cf 
   foreign key (products_id) 
   references Product
Hibernate: 

alter table MEMBER_PRODUCT 
   add constraint FK4ibylolqmostllrjdc147aowv 
   foreign key (Member_MEMBER_ID) 
   references Member

 

단방향인데 양방향 만들고 싶으면 product에서 아래와 같이 설정해준다.

@Entity
public class Product {

    @Id @GeneratedValue
    private Long id;

    private String name;

    @ManyToMany(mappedBy = "products")
    private List<Member> members = new ArrayList<>();

 

 

다대다 매핑의 한계를 보자.

  • 편리해 보이지만 실무에서 사용하면 안된다.

 

  • 연결 테이블이 단순히 연결만 하고 끝나지 않고

 

  • 주문시간, 수량 같은 데이터가 들어올 수 있다.

 

다대다 한계를 해결해주는 방법이 있다.

  • 연결 테이블용 엔티티 추가(연결 테이블을 엔티티로 승격)
  • @ManyToMany -> @OneToMany, @ManyToOne

 

MemberProduct를 아래와 같이 설정해주자. pk 하나만 잡고 가면 JPA mapping이 심플해진다.

package hellojpa;

import javax.persistence.*;
import java.time.LocalDateTime;

@Entity
public class MemberProduct {
    @Id
    @GeneratedValue
    private Long id;

    @ManyToOne
    @JoinColumn(name = "MEMBER_ID")
    private Member member;

    @ManyToOne
    @JoinColumn(name = "PRODUCT_ID")
    private Product product;

    private int count;
    private int price;

    private LocalDateTime orderDateTime;
}

 

Member에서는 MemberProduct를 이렇게 넣어주면 된다.

@Entity
public class Member {

    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    @Column(name = "USERNAME")
    private String username;

    @ManyToOne
    @JoinColumn(name = "TEAM_ID", insertable = false, updatable = false)
    private Team team;

    @OneToOne
    @JoinColumn(name = "LOCKER_ID")
    private Locker locker;

    @OneToMany(mappedBy = "member")
    private List<MemberProduct> memberProducts = new ArrayList<>();

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

<출처 김영한: 자바 ORM 표준 JPA 프로그래밍 - 기본편 >

https://www.inflearn.com/course/ORM-JPA-Basic/dashboard

 

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

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

www.inflearn.com