Search

๊ด€๊ณ„ ๋งคํ•‘

๊ด€๊ณ„ ๋งคํ•‘

JPA ๊ด€๊ณ„ ๋งคํ•‘ ๊ฐœ๋…

JPA์—์„œ ์—”ํ„ฐํ‹ฐ ๊ฐ„์˜ ๊ด€๊ณ„๋ฅผ ์„ค์ •ํ•  ๋•Œ ์—ฐ๊ด€ ๊ด€๊ณ„ ๋งคํ•‘์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ด€๊ณ„๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ถ„๋ฅ˜๋ฉ๋‹ˆ๋‹ค.
โ€ข
1:1 (One-To-One) โ†’ @OneToOne
โ€ข
1:N (One-To-Many) โ†’ @OneToMany
โ€ข
N:1 (Many-To-One) โ†’ @ManyToOne
โ€ข
N:M (Many-To-Many) โ†’ @ManyToMany
์ด ์ค‘ 1:N & N:1 ๊ด€๊ณ„๋Š” ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋ฉฐ, ์‹ค๋ฌด์—์„œ๋Š” ์ค‘๊ฐ„ ํ…Œ์ด๋ธ”์„ ์ด์šฉํ•œ Many-To-Many ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์ด ์ผ๋ฐ˜์ ์ž…๋‹ˆ๋‹ค.

๊ด€๊ณ„ ๋งคํ•‘ ์ฃผ์š” ์–ด๋…ธํ…Œ์ด์…˜

์–ด๋…ธํ…Œ์ด์…˜
์„ค๋ช…
@OneToOne
1:1 ๊ด€๊ณ„ ๋งคํ•‘
@OneToMany
1:N ๊ด€๊ณ„ ๋งคํ•‘ (ํ•œ ๊ฐœ์˜ ์—”ํ„ฐํ‹ฐ๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์—”ํ„ฐํ‹ฐ๋ฅผ ์ฐธ์กฐ)
@ManyToOne
N:1 ๊ด€๊ณ„ ๋งคํ•‘ (์—ฌ๋Ÿฌ ๊ฐœ์˜ ์—”ํ„ฐํ‹ฐ๊ฐ€ ํ•œ ๊ฐœ์˜ ์—”ํ„ฐํ‹ฐ๋ฅผ ์ฐธ์กฐ)
@ManyToMany
N:M ๊ด€๊ณ„ ๋งคํ•‘ (์ค‘๊ฐ„ ํ…Œ์ด๋ธ”์„ ๋ณ„๋„๋กœ ๋งŒ๋“ค์ง€ ์•Š๋Š” ๊ฒฝ์šฐ ์‚ฌ์šฉ)
@JoinColumn (name="foreign_key")
์™ธ๋ž˜ ํ‚ค(FK)๋ฅผ ์ง€์ •
@MappedBy
์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„์—์„œ ๋ฐ˜๋Œ€์ชฝ ๋งคํ•‘ ์„ค์ •
@Cascade(CascadeType.ALL)
์—ฐ๊ด€๋œ ์—”ํ„ฐํ‹ฐ์˜ ์ƒํƒœ ๋ณ€๊ฒฝ์„ ์ž๋™์œผ๋กœ ์ „ํŒŒ
@Fetch (FetchType.LAZY / FetchType.EAGER)
์—ฐ๊ด€ ๊ด€๊ณ„ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ์‹ ์ง€์ • (์ง€์—ฐ ๋กœ๋”ฉ, ์ฆ‰์‹œ ๋กœ๋”ฉ)

Boards & Files ๊ด€๊ณ„ ๋งคํ•‘ ์˜ˆ์ œ (1:N, N:1 ๊ด€๊ณ„ ์ ์šฉ)

Boards(๊ฒŒ์‹œ๊ธ€)์€ ์—ฌ๋Ÿฌ ๊ฐœ์˜ Files(ํŒŒ์ผ)์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ 1:N ๊ด€๊ณ„๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

Boards ์—”ํ„ฐํ‹ฐ (๊ฒŒ์‹œ๊ธ€)

import jakarta.persistence.*; import lombok.*; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import java.time.LocalDateTime; import java.util.List; @Entity @Table(name = "boards") @Getter @Setter @NoArgsConstructor @AllArgsConstructor @Builder public class Boards { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false, length = 100) private String title; @Column(nullable = false, columnDefinition = "TEXT") private String content; @OneToMany(mappedBy = "board", cascade = CascadeType.ALL, orphanRemoval = true) private List<Files> files; @CreatedDate @Column(updatable = false) private LocalDateTime createdAt; @LastModifiedDate private LocalDateTime updatedAt; }
Java
๋ณต์‚ฌ

Files ์—”ํ„ฐํ‹ฐ (ํŒŒ์ผ)

import jakarta.persistence.*; import lombok.*; @Entity @Table(name = "files") @Getter @Setter @NoArgsConstructor @AllArgsConstructor @Builder public class Files { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String fileName; @Column(nullable = false) private String filePath; @ManyToOne @JoinColumn(name = "board_id", nullable = false) private Boards board; }
Java
๋ณต์‚ฌ

๊ด€๊ณ„ ๋งคํ•‘ ์„ค๋ช…

Boards โ†’ Files (1:N ๊ด€๊ณ„)

โ€ข
Boards๋Š” ์—ฌ๋Ÿฌ ๊ฐœ์˜ Files๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.
โ€ข
@OneToMany(mappedBy = "board")๋ฅผ ํ†ตํ•ด ์–‘๋ฐฉํ–ฅ ๋งคํ•‘์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
โ€ข
cascade = CascadeType.ALL์„ ์‚ฌ์šฉํ•˜๋ฉด Boards๊ฐ€ ์‚ญ์ œ๋  ๋•Œ, ์—ฐ๊ด€๋œ Files๋„ ํ•จ๊ป˜ ์‚ญ์ œ๋ฉ๋‹ˆ๋‹ค.
โ€ข
orphanRemoval = true๋ฅผ ์„ค์ •ํ•˜๋ฉด Files ์—”ํ„ฐํ‹ฐ๊ฐ€ Boards์—์„œ ์ œ๊ฑฐ๋  ๊ฒฝ์šฐ, ์ž๋™์œผ๋กœ ์‚ญ์ œ๋ฉ๋‹ˆ๋‹ค.

Files โ†’ Boards (N:1 ๊ด€๊ณ„)

โ€ข
Files๋Š” ํ•˜๋‚˜์˜ Boards์— ์†ํ•ฉ๋‹ˆ๋‹ค.
โ€ข
@ManyToOne์„ ํ†ตํ•ด board_id๋ผ๋Š” ์™ธ๋ž˜ ํ‚ค(FK) ์ปฌ๋Ÿผ์„ ์ƒ์„ฑํ•˜๊ณ  Boards์™€ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.
โ€ข
@JoinColumn(name = "board_id")๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Boards ํ…Œ์ด๋ธ”์˜ ๊ธฐ๋ณธ ํ‚ค(id)์™€ ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค.

JPA ๊ด€๊ณ„ ๋งคํ•‘ Best Practice

์ง€์—ฐ ๋กœ๋”ฉ (FetchType.LAZY) ์ถ”์ฒœ
@ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "board_id", nullable = false) private Boards board;
Java
๋ณต์‚ฌ
โ€ข
๊ธฐ๋ณธ์ ์œผ๋กœ @ManyToOne์€ FetchType.EAGER(์ฆ‰์‹œ ๋กœ๋”ฉ)๋กœ ๋™์ž‘ํ•˜๋Š”๋ฐ, ์ด๋Š” ์„ฑ๋Šฅ ์ €ํ•˜๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ์Œ.
โ€ข
FetchType.LAZY๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋งŒ ์กฐํšŒํ•˜๋„๋ก ์„ค์ •.
CascadeType ์„ค์ •
@OneToMany(mappedBy = "board", cascade = CascadeType.ALL, orphanRemoval = true) private List<Files> files;
Java
๋ณต์‚ฌ
โ€ข
CascadeType.ALL์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ถ€๋ชจ ์—”ํ„ฐํ‹ฐ ์‚ญ์ œ ์‹œ ์ž์‹ ์—”ํ„ฐํ‹ฐ๋„ ํ•จ๊ป˜ ์‚ญ์ œ๋จ.
โ€ข
orphanRemoval = true๋ฅผ ์„ค์ •ํ•˜๋ฉด ๊ด€๊ณ„๊ฐ€ ๋Š์–ด์ง„ ์ž์‹ ์—”ํ„ฐํ‹ฐ๋Š” ์ž๋™ ์‚ญ์ œ๋จ.

์ •๋ฆฌ

1.
1:N ๊ด€๊ณ„์—์„œ @OneToMany(mappedBy = "board")๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ถ€๋ชจ(Boards)๊ฐ€ ์ž์‹(Files)์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
2.
N:1 ๊ด€๊ณ„์—์„œ @ManyToOne์„ ์‚ฌ์šฉํ•˜์—ฌ ์™ธ๋ž˜ ํ‚ค(FK) ์„ค์ •์„ ํ•ฉ๋‹ˆ๋‹ค.
3.
์ง€์—ฐ ๋กœ๋”ฉ (FetchType.LAZY)์„ ๊ธฐ๋ณธ ์„ค์ •์œผ๋กœ ์‚ฌ์šฉํ•˜์—ฌ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•ฉ๋‹ˆ๋‹ค.
4.
CascadeType ๋ฐ orphanRemoval ์˜ต์…˜์„ ํ™œ์šฉํ•˜์—ฌ ์—”ํ„ฐํ‹ฐ ์ˆ˜๋ช… ์ฃผ๊ธฐ๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
์ด์ œ Boards ์™€ Files ๊ฐ„์˜ ๊ด€๊ณ„ ๋งคํ•‘์ด ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!