Home > Backend Development > πŸ“š[Backend Development] @OneToManyλž€ λ¬΄μ—‡μΌκΉŒμš”?

πŸ“š[Backend Development] @OneToManyλž€ λ¬΄μ—‡μΌκΉŒμš”?
Backend Ddevelopment

β€œπŸ“š[Backend Development] @OneToManyλž€ λ¬΄μ—‡μΌκΉŒμš”?”

🍎 Intro.

  • @OneToManyλŠ” μΌλŒ€λ‹€(1:N) 관계λ₯Ό λ§€ν•‘ν•  λ•Œ μ‚¬μš©ν•˜λŠ” μ–΄λ…Έν…Œμ΄μ…˜μž…λ‹ˆλ‹€.
  • 즉, ν•˜λ‚˜(One)의 μ—”ν‹°ν‹°κ°€ μ—¬λŸ¬ 개(Many)의 μ—”ν‹°ν‹°λ₯Ό μ°Έμ‘°ν•˜λŠ” κ΅¬μ‘°μž…λ‹ˆλ‹€.

βœ…1️⃣ @OneToMany 예제.

  • κ²Œμ‹œκΈ€(Article)κ³Ό λŒ“κΈ€(Comment) 관계λ₯Ό 예둜 λ“€μ–΄λ³΄κ² μŠ΅λ‹ˆλ‹€.
  • ν•˜λ‚˜μ˜ κ²Œμ‹œκΈ€(Article)μ—λŠ” μ—¬λŸ¬ 개의 λŒ“κΈ€(Comment)이 달릴 수 μžˆμŠ΅λ‹ˆλ‹€.

1️⃣ Article μ—”ν‹°ν‹°(κ²Œμ‹œκΈ€)

@Entity
public class Article {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String title;
    private String content;
    
    @OneToMany(mappedBy = "article") // Comment μ—”ν‹°ν‹°μ˜ article ν•„λ“œκ°€ κ΄€κ³„μ˜ 주인
    private List<Comment> comments = new ArrayList<>();
    
    // Getter, Setter
}

2️⃣ Comment μ—”ν‹°ν‹°(λŒ“κΈ€)

@Entity
public class Comment {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String content;
    
    @ManyToOne
    @JoinColumn(name = "article_id") // comment ν…Œμ΄λΈ”μ— article_id FK 생성
    private Article article;
    
    // Getter, Setter
}

βœ…2️⃣ @OneToMany(mappedBy = β€œarticle”)의 의미

  • Comment μ—”ν‹°ν‹°μ˜ article ν•„λ“œλ₯Ό μ°Έμ‘°ν•˜μ—¬ μ–‘λ°©ν–₯ 관계λ₯Ό μ„€μ •ν•©λ‹ˆλ‹€.
  • μ™Έλž˜ ν‚€λ₯Ό κ΄€λ¦¬ν•˜λŠ” 주인은 Comment.article ν•„λ“œμ΄λ©°, Article μ—”ν‹°ν‹°λŠ” mappedByλ₯Ό 톡해 읽기 μ „μš©μž…λ‹ˆλ‹€.
  • 즉, Comment μ—”ν‹°ν‹°κ°€ κ΄€κ³„μ˜ 주인이고, Article μ—”ν‹°ν‹°μ—μ„œλŠ” 직접 FKλ₯Ό κ΄€λ¦¬ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. (➞ @JoinColumn이 Comment μͺ½μ—λ§Œ μžˆλŠ” 이유)

βœ…3️⃣ λ°μ΄ν„°λ² μ΄μŠ€ ν…Œμ΄λΈ” ꡬ쑰.

  • μœ„ μ½”λ“œλ₯Ό μ‹€ν–‰ν•˜λ©΄ λ‹€μŒκ³Ό 같은 ν…Œμ΄λΈ”μ΄ μƒμ„±λ©λ‹ˆλ‹€.

πŸ“Œ article ν…Œμ΄λΈ” (κ²Œμ‹œκΈ€)

id title content
1 β€œHello JPA” β€œJPA λ°°μš°κΈ°β€
2 β€œSpring Boot” β€œSpring 곡뢀”

πŸ“Œ comment ν…Œμ΄λΈ” (κ²Œμ‹œκΈ€μ— μ—°κ²°λœ λŒ“κΈ€, article_id FK 포함)

id content article_id(FK)
1 β€œμ’‹μ€ κΈ€μ΄λ„€μš”!” 1
2 β€œμœ μ΅ν•œ 정보 κ°μ‚¬ν•©λ‹ˆλ‹€.” 1
3 β€œSpring 졜고!” 2
  • πŸ“Œ article_id 컬럼이 κ²Œμ‹œκΈ€(Article)을 μ°Έμ‘°ν•˜λŠ” μ™Έλž˜ ν‚€(FK)μž…λ‹ˆλ‹€.
    • 즉, ν•˜λ‚˜μ˜ Articleμ—λŠ” μ—¬λŸ¬ 개의 Commentκ°€ 연결될 수 μžˆμŠ΅λ‹ˆλ‹€.

βœ…4️⃣ 데이터 쑰회.

βœ… νŠΉμ • κ²Œμ‹œκΈ€μ— μ†ν•œ λŒ“κΈ€ κ°€μ Έμ˜€κΈ°.

  • μ–‘λ°©ν–₯ 관계가 μ„€μ •λ˜μ–΄ μžˆμœΌλ―€λ‘œ, νŠΉμ • κ²Œμ‹œκΈ€μ— 달린 λŒ“κΈ€μ„ μ‰½κ²Œ κ°€μ Έμ˜¬ 수 μžˆμŠ΅λ‹ˆλ‹€.
Article article = entityManager.find(Article.class, 1L);
List<Comment> comments = article.getComments(); // ν•΄λ‹Ή κ²Œμ‹œκΈ€μ˜ λͺ¨λ“  λŒ“κΈ€ κ°€μ Έμ˜€κΈ°

πŸš€5️⃣ 단방ν–₯ @OneToMany vs μ–‘λ°©ν–₯ @OneToMany

1️⃣ 단방ν–₯ @OneToMany

@Entity
public class Article {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String title;
    private String content;
    
    @OneToMany
    @JoinColumn(name = "article_id") // FKλ₯Ό 직접 관리 (주인 μ—­ν• )
    private List<Comment> comments = new ArrayList<>();
    
    // Getter, Setter
}

βœ… μž₯점.

  • λ‹¨μˆœν•œ ꡬ쑰.
  • λΆˆν•„μš”ν•œ mappedBy 없이 @JoinColumn을 톡해 FK 직접 관리 κ°€λŠ₯

❌ 단점.

  • 데이터 μ‚½μž… μ‹œ 좔가적인 SQL μ‹€ν–‰ λ°œμƒ
  • @OneToMany 단방ν–₯ κ΄€κ³„μ—μ„œ @JoinColumn을 μ‚¬μš©ν•˜λ©΄ INSERT 쿼리가 두 번 싀행됨 (➞ λŒ“κΈ€ μ‚½μž… ν›„, κ²Œμ‹œκΈ€ ID μ—…λ°μ΄νŠΈ)

2️⃣ μ–‘λ°©ν–₯ @OneToMany + @ManyToOne

@Entity
public class Article {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String title;
    private String content;
    
    @OneToMany(mappedBy = "article") // Comment의 article ν•„λ“œλ₯Ό 주인으둜 μ„€μ •
    private List<Comment> comments = new ArrayList<>();
    
    // Getter, Setter
}

βœ… μž₯점.

  • μ„±λŠ₯ μ΅œμ ν™” κ°€λŠ₯ (FKλŠ” Comment.article이 관리).
  • INSERT 쿼리 싀행이 ν•œ 번만 λ°œμƒ.
  • 객체 κ·Έλž˜ν”„ 탐색이 νŽΈλ¦¬ν•¨ (article.getComments() κ°€λŠ₯).

❌ 단점

  • mappedBy둜 인해 데이터 μ €μž₯이 Comment μͺ½μ—μ„œ 이루어져야 함.

βœ…6️⃣ 정리

  • βœ”οΈ @OneToManyλŠ” ν•˜λ‚˜(One)의 μ—”ν‹°ν‹°κ°€ μ—¬λŸ¬ 개(Many)의 μ—”ν‹°ν‹°λ₯Ό μ°Έμ‘°ν•  λ•Œ μ‚¬μš©.
  • βœ”οΈ μ–‘λ°©ν–₯ κ΄€κ³„μ—μ„œλŠ” @OneToMany(mappedBy = β€œν•„λ“œλͺ…”) + @ManyToOne μ‘°ν•© μ‚¬μš©
  • βœ”οΈ 단방ν–₯ @OneToManyλ³΄λ‹€λŠ” μ–‘λ°©ν–₯을 μ‚¬μš©ν•˜λŠ” 것이 일반적
  • βœ”οΈ μ™Έλž˜ ν‚€(FK)λŠ” @ManyToOne μͺ½μ—μ„œ κ΄€λ¦¬ν•˜λ©°, @OneToManyλŠ” 읽기 μ „μš©
  • πŸ“Œ κ²Œμ‹œκΈ€-λŒ“κΈ€ κ΄€κ³„μ²˜λŸΌ 1:N 관계가 ν•„μš”ν•  λ•Œ @OneToManyλ₯Ό μ‚¬μš©ν•˜λ©΄ λ©λ‹ˆλ‹€.