Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
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 31
Tags
more
Archives
Today
Total
관리 메뉴

이지은님의 블로그

250214 - Java Spring 숙련: 효율적인 다대다 연관관계 매핑(@ManyToOne) 본문

TIL

250214 - Java Spring 숙련: 효율적인 다대다 연관관계 매핑(@ManyToOne)

queenriwon3 2025. 2. 14. 23:02

▷ 오늘 배운 것

늘 마찬가지로 배운 것은 늘 똑같이 까먹기 마련이다. 다시 리마인드 하기 위해서 JPA로 연관관계를 구현하는 방법에 대해 다시 블로그로 정리해볼 예정이다.

 

 

<<목차>>

1. 연관관계

    1) 1:N

    2) @ManyToOne, @OneToMany

    3) N:M 다대다 단/양방향 구현방법1 - @OneToMany + @ManyToOne

    4) N:M 다대다 단/양방향 구현방법2 - @ManyToOne

 

 

 


1. 연관관계

사실 연관관계는 하나밖에 없다.

엑셀시트를 생각해본다면 데이터는 오로지 하나밖에 들어간다.

결론은 테이블의 필드값으로 ‘리스트’는 본질적으로 들어가는 것이 불가능하다라는 것이다.

 

1) 1:N

@Entity
@Getter
@Table(name = "schedules")
@NoArgsConstructor
public class Schedules extends BaseEntity{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Setter
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private Users users;

    @OneToMany(mappedBy = "schedules", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<Comments> comments = new ArrayList<>();

    @Column(nullable = false)
    private String todoTitle;

    @Column(nullable = false, columnDefinition = "longtext")
    private String todoContents;

}
@OneToMany(mappedBy = "users", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Schedules> schedules = new ArrayList<>();

다음과 같은 코드를 보면

원래 데이터의 필드값으로 리스트를 넣는게 본질적으로 불가능한 것임에도 entity에 리스트를 작성하고 있다.(원래 데이터 베이스에는 없는 개념!)

 

 

 

2) @ManyToOne(fetch = FetchType.LAZY), @OneToMany를 어떻게 사용할 수 있을까?

 

@OneToMany는 @ManyToOne(fetch = FetchType.LAZY)이 없으면 존재할 수 없다.  —> @ManyToOne(fetch = FetchType.LAZY)

 

@ManyToOne(fetch = FetchType.LAZY) 더 개수가 많은 엔티티에 건다.(댓글)

 

N:M은 중간테이블 필요하다.

중간테이블에 @ManyToOne(fetch = FetchType.LAZY) 2개 선언하는 방법을 사용 —> 잘 사용하지 않는 방법(대체 할 수 있다.)

 

 

해야하는 설정
@ManyToOne @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = “상대방id”)
@OneToMany @OneToMany(mappedBy = “나자신”)

 

 

3) N:M 다대다 /양방향 구현방법1 - @OneToMany + @ManyToOne

@Entity
public class Member {
	@Id @GeneratedValue
	@Column(name = "member_id")
	private Long id;
	private String name;

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

@Entity
public class Product {
	@Id @GeneratedValue
	@Column(name = "product_id")
	private Long id;
	private String name;

	// 양방향의 경우 추가
	// @ManyToMany(mappedBy = "products")
	// private List<Member> members = new ArrayList<>();
}

여러명의 member가 여러개의 product와 연결되는 다대다 상황이다.

@ManyToMany를 사용해주어 여러개의 products list를 연결해준다.

위에서 일대다를 사용한 @ManyToOne@JoinColumn(name = “{}_id”)를 사용한데 반해,

@ManyToMany는 테이블과 조인을 해야하기 때문에 @JoinTable(name = “{}_{}”)로 테이블을 연결해야 한다.

 

하지만 실무에서는 이를 사용하지 않는다.

 

@ManyToMany 다대다를 사용해서 중간테이블을 만들었다가 복잡한 조인쿼리가 발생하게 된다.(사이드 이팩트)

 

그렇다면 어떻게 해결할 수 있을까?

—> @OneToMany, @ManyToOne 를 적절히 사용하자.

—> 중간테이블 역할을 하는 중간 엔티티를 만들어주자.

 

@Entity
public class Member {
	@Id @GeneratedValue
	@Column(name = "member_id")
	private Long id;
	private String name;

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

@Entity
public class MemberProduct {
	@Id @GeneratedValue
	@Column(name = "member_product_id")
	private Long id;

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

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

@Entity
public class Product {
	@Id @GeneratedValue
	@Column(name = "product_id")
	private Long id;
	private String name;

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

 

위 코드의 구조는 다음과 같다.

Member ➡️ MemberProduct ⬅️ Product

MemberProduct라는 중간 테이블을 엔티티로 만들어 관리하는 것이다. 다대다의 단점을 극복하기 위해서 좀 더 관리하기 편한 @OneToMany + @ManyToOne의 중간엔티티를 만들어서 관리 해보도록 하자.

 

 

4) N:M 다대다 /양방향 구현방법2 - @ManyToOne

import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@NoArgsConstructor
@Table(name = "book_authors")
public class BookAuthor {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "book_id", nullable = false)
    private Book book;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "author_id", nullable = false)
    private Author author;

    public BookAuthor(Book book, Author author) {
        this.book = book;
        this.author = author;
    }
}

 

Book과 Author의 중간테이블인 BookAuthor를 만들고,

BookAuthor 테이블에 Book Author @ManyToOne으로 매핑하면 

오직 순수하게 @ManyToOne 사용하고도 중간엔티티를 만들 있으며, 효율적으로 관리할 있다.