JPA에서의 다양한 연관관계 설정

MONA·2024년 3월 12일

나혼공

목록 보기
15/92

이전에 실습했던 간단한 게시판 만들기 프로젝트에서는 일대다, 다대다 관계를 설정해 본 적이 없었다. 하지만 현재 진행하는 프로젝트 ERD에는 일대다, 다대다 관계가 필수였기에 관련 개념을 알아보았다.

관계 설정시 확인해야 할 것들

  1. 관계의 다중성

    1. N:1 - @manyToOne
    2. 1:N - @OneToMany
    3. 1:1 - @OneToOne
    4. N:M - @ManyToMany
  2. 단방향인가, 양방향인가

  3. 양방향일 경우 두 테이블 중 외래키를 관리할 테이블을 정해야 한다.

    1. 외래키를 관리하는 테이블: 연관관계 주도 테이블
    2. 외래키를 관리하지 않는 테이블은 외래키에 영향을 주지 않음.

N:1 관계

단방향 N:1

회원 한 명이 여러 가게를 소유할 수 있다
가게들(N):회원(1) => 가게(N)가 회원(1)을 참조

가게(N)에서 외래키를 관리한다.

외래키가 존재하는 곳(가게)에서 참조를 걸고 연관관계를 매핑한다.
가게 테이블에서 @JoinColumn(name= "member_id") 어노테이션 설정

가게 테이블에서는 회원을 참조할 수 있지만, 회원 테이블에서는 가게를 참조할 수 없다.

양방향 N:1

회원 한 명이 여러 가게를 소유할 수 있다.
가게 테이블에서 회원을 참조하고, 회원 테이블에서도 가게 테이블을 참조할 수 있다.
회원 테이블에서 @OneToMany(mappedBy="store")을 설정해준다.
리스트로 가져온다.

1:N 관계

단방향 1:N

회원(1)에서 외래키를 관리한다.
회원(1)에서는 가게(N)들을 조회할 수 있지만 가게(N)에서는 회원(1)을 알 수 없다.
@JoinColumn을 꼭 사용하거나 중간에 테이블을 하나 두는 방식으로 관리해야한다.
연관관계 관리를 위해 추가적으로 UPDATE SQL이 실행된다.

양방향 1:N

두 테이블 모두에 @JoinColumn이 붙는다...(둘다 연관관계의 주체가 됨)
대신 @JoinColumn(insertable=false, updatable=false)이렇게 설정해서 읽기 전용으로 참고만 해야한다.

제대로 이해한 지 모르겠다.

1:1 관계

단방향 1:1

N:1 단방향 관계에서 어노테이션만 달라진 느낌.
외래키에 유니크 제약조건을 줘야 한다.
연관관계의 주인이 되는 클래스에 @OneToOne, @JoinColumn 을 적용한다.

양방향 1:1

외래키가 있는 곳이 연관관계의 주체이다.
외래키가 있는 반대 테이블의 필드에 @OneToOne(mappedBy="테이블명")를 지정해준다.


적용

가게 관련 기본 CRUD 기능을 수행하는 코드 작성

domain

domain에는 DB 테이블과 매칭되는 class 파일들이 작성된다.
가게 entity 생성을 위해 Store.java 파일을 생성하고 작성한다.

해서 위에 학습한 개념을 적용해 클래스를 작성해보았다.

case 1
: Store 객체의 입장에서
한 명의 member는 여러 개의 store를 가질 수 있다. member(1) : store(N)
하나의 registrationNumber(사업자등록번호)는 여러 개의 store에 적용될 수 있다. registrationNumber(1) : store(N)
하나의 store는 여러 개의 businessHour를 가질 수 있다. store(1) : businessHour(N)

// Store.java

@Entity
@NoArgsConstructor
@AllArgsConstructor
@Getter
public class Store {

    @Id // id 필드를 기본키로 지정한다
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 기본키를 자동으로 1씩 증가시킨다
    @Column(name = "store_id", updatable = false, nullable = false)
    private Long id;

    @Column(name = "name", nullable = false)
    private name;

    // ... 생략

    @ManyToOne
    @JoinColumn(name = "member_id")
    private Member member;
    // member 하나에 store는 여러 개 존재할 수 있음

    @ManyToOne
    @JoinColumn(name = "registration_number_id")
    private RegistrationNumber registrationNumber;
    // registrationNumber 하나에 store는 여러 개 존재할 수 있음

    @OneToMany(mappedBy = "store")
    private List<BusinessHour> businessHours = new ArrayList<>();
    // store 하나에 businessHour는 여러 개 존재할 수 있음
}
// BusinessHour.java

@Entity
@NoArgsConstructor
@AllArgsConstructor
@Getter
public class BusinessHour {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "business_hour_id", updatable = false, nullable = false)
    private Long id;

    private day; // 요일

    // ... 생략

    @ManyToOne
    @JoinColumn(name = "store_id")
    private Store store;
    // store 하나에 영업시간이 여러 개(요일별로) 존재할 수 있음.

}

case 2
: 내가 보유한 기프티콘(socon) 하나는 하나의 게시글(sogon)과 연결될 수 있다.
게시글(sogon)이 외래키를 갖고 기프티콘 엔터티(socon)를 참조한다.
이 경우 기프티콘 엔터티(socon)에서는 외래 키를 저장하지 않는다.

  1. 단방향 설정
    게시글 엔터티에서는 기프티콘 엔터티를 조회할 수 있지만, 기프티콘 엔터티는 게시글 엔터티를 확인할 수 없다.

@Entity
@AllArgsConstructor
@NoArgsConstructor
@Getter
public class Sogon {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "sogon_id", nullable = false)
    private Long id;

    // ... 생략

    @OneToOne
    private MySocon mySocon;
}


@Entity
@NoArgsConstructor
@AllArgsConstructor
@Getter
public class MySocon {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "socon_id", updatable = false, nullable = false)
    private Long id;

    // ... 생략

}

  1. 양방향 설정
    서로가 서로의 테이블을 참조할 수 있다.
    게시글 엔터티에서는 기프티콘 엔터티를 기프티콘 필드로 참조하고,
    기프티콘 엔터티에서는 게시글 엔터티를 게시글 필드로 참조한다.

@Entity
@AllArgsConstructor
@NoArgsConstructor
@Getter
public class Sogon {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "sogon_id", nullable = false)
    private Long id;

    // ... 생략

    @OneToOne
    private MySocon mySocon;
}

@Entity
@NoArgsConstructor
@AllArgsConstructor
@Getter
public class MySocon {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "socon_id", updatable = false, nullable = false)
    private Long id;

    // ... 생략

}

profile
고민고민고민

0개의 댓글