Notice
Recent Posts
Recent Comments
Link
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
Archives
Today
Total
관리 메뉴

차곡차곡 쌓아가는 개발일기

[JPA]N:M 연관관계 매핑 본문

Spring/JPA

[JPA]N:M 연관관계 매핑

KimTaeO 2023. 8. 10. 23:59

N:M

  • 관계형 DB는 정규화된 테이블 2개로 N:M 관계를 표현할 수 없다. 따라서 중간에 연결 테이블을 추가해 N:M 관계를 1:N, N:1 관계로 풀어낼 수 있다
  • 하지만 객체는 테이블과 다르게 객체 2개로 N:M 관계를 풀어낼 수 있다

N:M 단방향

  • @ManyToMany와 @JoinTable을 통해 연결테이블을 관리하지 않고도 다대다 관계를 풀어낼 수 있다
  • 코틀린으로 N:M 양방향 구현 예시 코드
  • @Entity class Library( @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "library_id") var id: Long = 0, val name: String, @ManyToMany @JoinTable(name = "LIBRARY_LIBRARIAN", joinColumns = @JoinColumn(name = "LIBRARIAN_ID"), inverseJoinColumns = @JoinColumn(name = "LIBRARY_ID")) val librarians: MutableList<Librarian> = mutableListOf() )

@Entity
class Librarian(
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "librarian_id", nullable = false)
var id: Long = 0,

@Column(nullable = false)
val name: String

)

* joinColumns 파라미터는 현재 매핑하려는 엔티티의 JoinColumn 정보로 필요로 한다
* inverseJoinColumns 파라미터는 현재 매핑하려는 엔티티의 반대 엔티티의 JoinColumn 정보를 필요로 한다

### N:M 양방향
* mappedBy를 통해 연관관계의 주인을 정해주는 것 말고는 단방향과 차이가 없다
* 코틀린으로 N:M 양방향 구현 예시 코드
```Kotlin
@Entity
class Library(
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "library_id")
    var id: Long = 0,

    val name: String,

    @ManyToMany
    @JoinTable(name = "LIBRARY_LIBRARIAN",
    joinColumns = @JoinColumn(name = "LIBRARIAN_ID"), 
    inverseJoinColumns = @JoinColumn(name = "LIBRARY_ID"))
    val librarians: MutableList<Librarian> = mutablelistof()
)

@Entity
class Librarian(
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "librarian_id", nullable = false)
    var id: Long = 0,

    @Column(nullable = false)
    val name: String

    @ManyToMany(mappedBy = "librarians")
    val libraries: MutableList<Library> = mutablelistof()
)

다대다 연관관계 매핑의 문제점

    • @ManyToMany 어노테이션을 사용하면 연결 테이블에 컬럼을 추가하거나 하는 등 테이블을 직접 관리할 수 없게된다
      @Entity
      class Library(
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        @Column(name = "library_id")
        var id: Long = 0,
      
        val name: String,
      
        @OneToMany(mappedBy = "library")
        val librarians: MutableList<Librarian> = mutableListOf<Librarian>()
      )
      
    •  

따라서 연결 엔티티를 직접 추가해 1:N, N:1 연관관계를 매핑한 다음 N:M 처럼 사용하는 것이다

  •  

@Entity
class Library_Librarian(
@Id
@ManyToOne
@JoinColumn(name = "LIBRARY_ID")
val library: Library,

@Id 
@ManyToOne
@JoinColumn(name = "LIBRARIAN_ID")
val librarian: Librarian

)

@Entity
class Librarian(
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "librarian_id", nullable = false)
var id: Long = 0,

@Column(nullable = false)
val name: String,

@OneToMany(mappedBy = "librarian")
val libraries: MutableList<Library> = mutableListOf()

)

data class LibraryLibrarianId(
val library: String,
val librarian: String
): Serializable


* 복합 기본키를 통해 두 테이블의 기본키를 연결 테이블의 기본키로 사용하였다 이를 식별 관계라고 한다
    * 식별자 클래스를 만들어 @IdClass 어노테이션에 지정하였고 식별자 클래스는 Serializable을 구현해야 하며, equals와 hashCode 메소드를 구현해야 하고, 식별자 클래스는 public이어야 한다

> 하지만 이러한 방식은 식별자 클래스를 만들고 equals와 hashCode를 구현하고, @IdClass 등을 사용해야 하는 번거로움을 가지고 있는데 이러한 복잡함을 해결하기 위해서는 새로운 기본 키를 사용하는 방법이 있다

### N:M 새로운 기본 키 사용
* 복합 키를 사용하지 않고 데이터베이스에서 자동으로 생성되는 키를 사용하여 간단히 매핑을 완성할 수 있다 이를 비식별 관계라고 한다
* 코틀린으로 N:M 새로운 기본 키 사용 예시 코드
```Kotlin
@Entity
class Library(
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "library_id")
    var id: Long = 0,

    val name: String,

    @OneToMany(mappedBy = "library")
    val librarians: MutableList<Librarian> = mutableListOf<Librarian>()
)

@Entity
class Library_Librarian(
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    val id: Long = 0L,

    @ManyToOne
    @JoinColumn(name = "LIBRARY_ID")
    val library: Library,

    @ManyToOne
    @JoinColumn(name = "LIBRARIAN_ID")
    val librarian: Librarian
)

@Entity
class Librarian(
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "librarian_id", nullable = false)
    var id: Long = 0,

    @Column(nullable = false)
    val name: String,

    @OneToMany(mappedBy = "librarian")
    val libraries: MutableList<Library> = mutableListOf()
)

오늘은 이렇게 N:M 매핑 방식을 정리해 보고, 각각의 방법의 특징들을 알아보았는데 자주 사용해본 비식별 관계로 N:M 매핑을 하는것 말고도 여러가지 방법들을 알아보는걸 통해 내 견해가 더 넓어진 시간이었던 것 같아서 좋았다

'Spring > JPA' 카테고리의 다른 글

[JPA] 객체와 테이블의 연관관계 매핑(1:1, 1:N, N:1)  (0) 2023.07.31