Home > Spring > πŸƒ[Spring] JPA 연관관계에 λŒ€ν•œ 좔가적인 κΈ°λŠ₯λ“€μ—λŠ” 무엇이 μžˆμ„κΉŒμš”? - `@OneToMany`

πŸƒ[Spring] JPA 연관관계에 λŒ€ν•œ 좔가적인 κΈ°λŠ₯λ“€μ—λŠ” 무엇이 μžˆμ„κΉŒμš”? - `@OneToMany`
Spring Framework

πŸƒ[Spring] JPA 연관관계에 λŒ€ν•œ 좔가적인 κΈ°λŠ₯λ“€μ—λŠ” 무엇이 μžˆμ„κΉŒμš”? - @OneToMany

  • @OneToManyλŠ” 1:N(μΌλŒ€λ‹€) 관계λ₯Ό λ‚˜νƒ€λ‚΄λŠ” μ–΄λ…Έν…Œμ΄μ…˜μž…λ‹ˆλ‹€.
    • μ΄λŠ” ν•œ 개의 μ—”ν‹°ν‹°κ°€ μ—¬λŸ¬ 개의 엔티티와 관계λ₯Ό λ§ΊλŠ” μƒν™©μ—μ„œ μ‚¬μš©λ©λ‹ˆλ‹€.
      • 예λ₯Ό λ“€μ–΄, ν•˜λ‚˜μ˜ νŒ€(Team)이 μ—¬λŸ¬ λͺ…μ˜ νšŒμ›(User)κ³Ό 관계λ₯Ό λ§ΊλŠ” 경우 @OneToManyλ₯Ό μ‚¬μš©ν•˜μ—¬ ν‘œν˜„ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

1️⃣ @OneToMany μ–΄λ…Έν…Œμ΄μ…˜ κΈ°λ³Έ κ°œλ….

  • μΌλŒ€λ‹€ κ΄€κ³„μ—μ„œ 1(One) μͺ½μ΄ λ‹€μˆ˜(N) μͺ½μ„ μ°Έμ‘°ν•  λ•Œ @OneToMany μ–΄λ…Έν…Œμ΄μ…˜μ„ μ‚¬μš©ν•©λ‹ˆλ‹€.
  • 보톡 λ‹€μˆ˜(N) μͺ½μ΄ μ™Έλž˜ ν‚€λ₯Ό 가지고 μžˆμ–΄ μΌλŒ€λ‹€ κ΄€κ³„μ—μ„œ λ‹€μˆ˜ μͺ½μ΄ κ΄€κ³„μ˜ 주인이 λ©λ‹ˆλ‹€.
  • @OneToMany μ–΄λ…Έν…Œμ΄μ…˜μ€ mappedBy 속성과 ν•¨κ»˜ μ‚¬μš©ν•˜μ—¬ 주인이 μ•„λ‹˜μ„ λͺ…μ‹œν•˜κ³ , 관계λ₯Ό 읽기 μ „μš©μœΌλ‘œ μ„€μ •ν•©λ‹ˆλ‹€.

πŸ‘‰ μ˜ˆμ‹œ: νŒ€(Team)κ³Ό νšŒμ›(User) κ°„μ˜ 1:N 관계

  • νŒ€(Team) 엔티티와 νšŒμ›(User) μ—”ν‹°ν‹°κ°€ 1:N 관계λ₯Ό κ°€μ§ˆ λ•Œ, Team μ—”ν‹°ν‹°μ—μ„œ μ—¬λŸ¬ User μ—”ν‹°ν‹°λ₯Ό μ°Έμ‘°ν•˜λ„λ‘ μ„€μ •ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
@Entity
public class Team {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    
    @OneToMany(mappedBy = "team") // Team이 μ—°κ΄€κ΄€μ˜ˆμ˜ 주인이 μ•„λ‹˜μ„ λͺ…μ‹œ
    private List<User> users = new ArrayList<>();
    
    // getter, setter
}

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    
    @ManyToOne // λ‹€λŒ€μΌ κ΄€κ³„μ—μ„œ Userκ°€ μ—°κ΄€κ΄€κ³„μ˜ 주인
    @JoinColumn(name = "team_id") // μ™Έλž˜ ν‚€ 컬럼 지정
    private Team team;
    
    // getter, setter
}

πŸ‘‰ μ½”λ“œ μ„€λͺ…

  • Team μ—”ν‹°ν‹°λŠ” @OneToMany(mappedBy = "team")을 μ‚¬μš©ν•˜μ—¬ Userμ™€μ˜ κ΄€κ³„μ—μ„œ 주인이 μ•„λ‹˜μ„ λͺ…μ‹œν•©λ‹ˆλ‹€.
    • mappedBy μ†μ„±μ˜ κ°’ β€œteam”은 User μ—”ν‹°ν‹°μ˜ team ν•„λ“œλ₯Ό κ°€λ¦¬ν‚΅λ‹ˆλ‹€.
  • User μ—”ν‹°ν‹°λŠ” @ManyToOneκ³Ό @JoinColumn(name = "team_id")을 μ‚¬μš©ν•˜μ—¬ μ™œλž˜ ν‚€ team_id μ»¬λŸΌμ„ μƒμ„±ν•˜λ©°, κ΄€κ³„μ˜ 주인이 λ©λ‹ˆλ‹€.

2️⃣ 단방ν–₯κ³Ό μ–‘λ°©ν–₯ @OneToMany

1️⃣ 단방ν–₯ @OneToMany

  • @OneToManyλ₯Ό μ‚¬μš©ν•˜μ—¬ 단방ν–₯으둜 관계λ₯Ό μ„€μ •ν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.
    • ν•˜μ§€λ§Œ λ°μ΄ν„°λ² μ΄μŠ€μ— 좔가적인 쑰인 ν…Œμ΄λΈ”μ΄ μƒμ„±λ˜λ―€λ‘œ, 보톡 μ„±λŠ₯μƒμ˜ 이유둜 많이 μ‚¬μš©ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

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

  • @OneToMany와 @ManyToOne을 ν•¨κ»˜ μ‚¬μš©ν•˜μ—¬ μ–‘λ°©ν–₯으둜 μ„€μ •ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
    • 이 경우 @ManyToOne μͺ½μ΄ μ™Έλž˜ ν‚€λ₯Ό κ°€μ§€λ―€λ‘œ μ—°κ΄€κ΄€κ³„μ˜ 주인이 λ©λ‹ˆλ‹€.

3️⃣ @OneToMany μ‚¬μš© μ‹œ μ£Όμ˜μ‚¬ν•­.

1️⃣ 지연 λ‘œλ”©(Lazy Loading)

  • @OneToMany의 κΈ°λ³Έ λ‘œλ”© μ „λž΅μ€ 지연 λ‘œλ”©μž…λ‹ˆλ‹€.
    • 즉, μ—”ν‹°ν‹°λ₯Ό μ‘°νšŒν•  λ•ŒλŠ” μ¦‰μ‹œ λ‘œλ”©ν•˜μ§€ μ•Šκ³ , μ‹€μ œλ‘œ μ ‘κ·Όν•  λ•Œ 데이터λ₯Ό λ‘œλ“œν•˜μ—¬ μ„±λŠ₯을 μ΅œμ ν™”ν•©λ‹ˆλ‹€.

2️⃣ Cascade μ˜΅μ…˜ μ‚¬μš©.

  • CascadeType.ALL μ΄λ‚˜ CascadeType.REMOVE λ“±μ˜ μ˜΅μ…˜μ„ μ‚¬μš©ν•˜λ©΄, λΆ€λͺ¨ μ—”ν‹°ν‹°λ₯Ό μ €μž₯ν•˜κ±°λ‚˜ μ‚­μ œν•  λ•Œ μžμ‹ 엔티티도 ν•¨κ»˜ μ €μž₯/μ‚­μ œλ©λ‹ˆλ‹€.
    • μ£Όμ˜ν•΄μ„œ μ‚¬μš©ν•΄μ•Ό ν•˜λ©°, λΆˆν•„μš”ν•˜κ²Œ μžμ‹ μ—”ν‹°ν‹°κ°€ μ‚­μ œλ˜μ§€ μ•Šλ„λ‘ ν•©λ‹ˆλ‹€.

3️⃣ N+1 문제.

  • @OneToManyλŠ” λ‹€μˆ˜μ˜ μžμ‹ μ—”ν‹°ν‹°λ₯Ό κ°€μ Έμ˜¬ λ•Œ N+1 λ¬Έμ œκ°€ λ°œμƒν•  수 μžˆμœΌλ―€λ‘œ, ν•„μš”ν•œ 경우 페치 쑰인(fetch join)을 μ‚¬μš©ν•΄ ν•΄κ²°ν•΄μ•Ό ν•©λ‹ˆλ‹€.

πŸ‘‰ μ˜ˆμ‹œ 상황.

  • νŒ€(Team)κ³Ό νšŒμ›(User) : ν•œ νŒ€μ— μ—¬λŸ¬ λͺ…μ˜ νšŒμ›μ΄ μ†Œμ†λ  수 있음.
  • μ£Όλ¬Έ(Order)κ³Ό μƒν’ˆ(Item) : ν•œ 주문에 μ—¬λŸ¬ μƒν’ˆμ΄ 포함될 수 있음.

4️⃣ μš”μ•½.

  • @OneToManyλŠ” 1:N 관계λ₯Ό ν‘œν˜„ν•˜λ©°, 보톡 mappedBy 속성을 μ‚¬μš©ν•˜μ—¬ μ—°κ΄€κ΄€κ³„μ˜ 주인이 μ•„λ‹˜μ„ λͺ…μ‹œν•©λ‹ˆλ‹€.
    • 이 μ–΄λ…Έν…Œμ΄μ…˜μ„ 톡해 λΆ€λͺ¨-μžμ‹ 관계λ₯Ό μ„€μ •ν•˜μ—¬ νŽΈλ¦¬ν•˜κ²Œ λ‹€μˆ˜μ˜ μ—”ν‹°ν‹°λ₯Ό 관리할 수 μžˆμ§€λ§Œ, μ„±λŠ₯ 및 데이터 일관성을 μœ μ§€ν•˜κΈ° μœ„ν•΄ λ‘œλ”© μ „λž΅κ³Ό Cascade μ˜΅μ…˜μ„ μ£Όμ˜ν•΄μ„œ μ„€μ •ν•΄μ•Ό ν•©λ‹ˆλ‹€.