๊ด๊ณ ๋งคํ
JPA ๊ด๊ณ ๋งคํ ๊ฐ๋
JPA์์ ์ํฐํฐ ๊ฐ์ ๊ด๊ณ๋ฅผ ์ค์ ํ ๋ ์ฐ๊ด ๊ด๊ณ ๋งคํ์ ์ฌ์ฉํฉ๋๋ค. ๊ด๊ณ๋ ๋ค์๊ณผ ๊ฐ์ด ๋ถ๋ฅ๋ฉ๋๋ค.
โข
โข
โข
โข
์ด ์ค 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
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "board_id", nullable = false)
private Boards board;
Java
๋ณต์ฌ
โข
๊ธฐ๋ณธ์ ์ผ๋ก @ManyToOne์ FetchType.EAGER(์ฆ์ ๋ก๋ฉ)๋ก ๋์ํ๋๋ฐ, ์ด๋ ์ฑ๋ฅ ์ ํ๋ฅผ ์ผ์ผํฌ ์ ์์.
โข
FetchType.LAZY๋ฅผ ์ฌ์ฉํ์ฌ ์ค์ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ ๋๋ง ์กฐํํ๋๋ก ์ค์ .
@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 ๊ฐ์ ๊ด๊ณ ๋งคํ์ด ์๋ฃ๋์์ต๋๋ค! 