관리 메뉴

The Nirsa Way

[Spring Data JPA] 연관 관계 매핑 (3) : 기타 관계 (@OneToOne, @ManyToMany) 본문

Development/Spring Data JPA

[Spring Data JPA] 연관 관계 매핑 (3) : 기타 관계 (@OneToOne, @ManyToMany)

KoreaNirsa 2025. 7. 17. 11:43
반응형

 

기타 관계 (@OneToOne, @ManyToMany)

ManyToOne, OneToMany 보다 상대적으로 사용 빈도가 낮은 OneToOne, ManyToMany를 기타 관계라고 표현하기도 합니다. 네이밍 그대로 1:1, N:N 관계를 가지는 어노테이션이며 특히 ManyToMany의 경우 중간 매핑 테이블을 따로 분리하는 것이 좀 더 좋은 케이스인 경우가 많으므로 거의 사용되지 않습니다.

 

1. @OneToOne

한 명의 학생(student)은 하나의 사물함(rocker)만 사용해야하는 경우와 같이 1:1로 매핑되어야 하는 상황에서 사용되는 어노테이션 입니다. 단방향을 기준으로 작성된 엔티티는 다음과 같습니다.

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

    private String name;

    @OneToOne
    @JoinColumn(name = "locker_id")
    private Locker locker;
}
@Entity
public class Locker {
    @Id @GeneratedValue
    private Long id;

    private String location;
}

 

위의 엔티티를 기준으로 테이블을 생성하면 아래와 같은 DDL이 완성되는데, 엔티티에서는 locker_id를 기준으로 1:1 매핑인 상태이므로 SQL의 제약조건에도 UNIQUE를 주어야 합니다.

만약, UNIQUE가 없다면 중복된 데이터가 삽입이 가능해지고 이로 인해 1:N 관계가 될 가능성이 있기 때문에 UNIQUE를 주어 1:1 관계를 보장 받아야 합니다.

CREATE TABLE locker (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    location VARCHAR(255)
);

CREATE TABLE student (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(255),
    locker_id BIGINT UNIQUE,
    CONSTRAINT fk_student_locker FOREIGN KEY (locker_id) REFERENCES locker(id)
);

 

2. @ManyToMany

N:N 관계의 경우 거의 사용되지 않는 케이스이며 이러한 관계를 가질 경우 중간 테이블 확장이 불가능하여 다시 설계 및 구조를 변경해야 하는 상황이 발생할 수 있습니다. ManyToMany는 간단히 엔티티와 DDL 예시만 살펴보고 넘어가도록 하겠습니다. 

이번에는 DDL을 먼저 살펴볼텐데, member_role이라는 테이블이 각각 member와 role을 참조하는 식별 관계인 케이스 입니다.

CREATE TABLE member (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(255)
);

CREATE TABLE role (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    role_name VARCHAR(255)
);

CREATE TABLE member_role (
    member_id BIGINT,
    role_id BIGINT,
    PRIMARY KEY (member_id, role_id),
    FOREIGN KEY (member_id) REFERENCES member(id),
    FOREIGN KEY (role_id) REFERENCES role(id)
);

 

엔티티는 아래와 같이 진행되며 ManyToMany 어노테이션을 사용하여 N:N 관계임을 정의하고, N:N을 표현하기 위한 중간 조인 테이블(member_role)을 설정합니다. 이후 각각 JoinColumn을 사용하여 외래키를 지정해주는 방식입니다.

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

    private String username;

    @ManyToMany
    @JoinTable(
        name = "member_role",
        joinColumns = @JoinColumn(name = "member_id"),
        inverseJoinColumns = @JoinColumn(name = "role_id")
    )
    private List<Role> roles = new ArrayList<>();
}
@Entity
public class Role {
    @Id @GeneratedValue
    private Long id;

    private String roleName;
}

 

사실, 설계할 때 이러한 구조보다는 아래와 같이 N:1 관계를 지정해주는 중간 엔티티(MemberRole)을 사용하는 방식이 권장됩니다.

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

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

    @ManyToOne
    @JoinColumn(name = "role_id")
    private Role role;
}
@Entity
public class Member {
    @Id @GeneratedValue
    private Long id;

    private String username;

    @OneToMany(mappedBy = "member")
    private List<MemberRole> memberRoles = new ArrayList<>();
}
@Entity
public class Role {
    @Id @GeneratedValue
    private Long id;

    private String name;

    @OneToMany(mappedBy = "role")
    private List<MemberRole> memberRoles = new ArrayList<>();
}

 

반응형