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

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

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

1️⃣ 연관관계 주인 효과.

  • JPAμ—μ„œ μ—°κ΄€κ΄€κ³„μ˜ 주인으둜 μ„€μ •λœ μ—”ν‹°ν‹°λ§Œ λ°μ΄ν„°λ² μ΄μŠ€μ˜ μ™Έλž˜ ν‚€λ₯Ό λ°˜μ˜ν•  수 μžˆλŠ” 효과λ₯Ό λ§ν•©λ‹ˆλ‹€.
  • JPAμ—μ„œλŠ” μ—°κ΄€κ΄€κ³„μ˜ 주인(Owner)κ³Ό 주인이 μ•„λ‹Œ μ—”ν‹°ν‹°(Non-owner)λ₯Ό κ΅¬λΆ„ν•˜μ—¬, λ°μ΄ν„°λ² μ΄μŠ€μ— μ™Έλž˜ ν‚€λ₯Ό μ €μž₯ν•˜κ±°λ‚˜ μ—…λ°μ΄νŠΈν•  λ•Œ 주인으둜 μ„€μ •λœ μ—”ν‹°ν‹°λ§Œ μ‹€μ œ SQL에 λ°˜μ˜λ˜λ„λ‘ ν•©λ‹ˆλ‹€.

2️⃣ 연관관계 주인 효과의 λ™μž‘ 방식.

  • μ–‘λ°©ν–₯ μ—°κ΄€κ΄€κ³„μ—μ„œ 두 μ—”ν‹°ν‹°κ°€ μ„œλ‘œλ₯Ό μ°Έμ‘°ν•  λ•Œ, μ–΄λŠ ν•œμͺ½μ„ β€œμ—°κ΄€κ΄€κ³„μ˜ μ£ΌμΈβ€μœΌλ‘œ μ„€μ •ν•˜κ³ , κ·Έ μ£ΌμΈμ—μ„œλ§Œ μ™Έλž˜ ν‚€ 값을 μ €μž₯ν•˜κ±°λ‚˜ λ³€κ²½ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
    • 주인이 μ•„λ‹Œ μͺ½μ—μ„œλŠ” mappedBy 속성을 톡해 주인이 μ•„λ‹˜μ„ λͺ…μ‹œν•˜κ³ , μ΄λŠ” 읽기 μ „μš©μœΌλ‘œ μ·¨κΈ‰λ©λ‹ˆλ‹€.
      • 이λ₯Ό 톡해 JPAλŠ” μ€‘λ³΅λœ μ™Έλž˜ ν‚€ μ €μž₯을 λ°©μ§€ν•˜κ³ , μ‹€μ œ μ™Έλž˜ ν‚€ 관리λ₯Ό μΌκ΄€λ˜κ²Œ μ²˜λ¦¬ν•©λ‹ˆλ‹€.

πŸ‘‰ μ˜ˆμ‹œ: 1:1 κ΄€κ³„μ—μ„œμ˜ 연관관계 주인 효과.

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    
    @OneToOne
    @JoinColumn(name = "user_profile_id") // μ™Έλž˜ ν‚€ 관리
    private UserProfile userProfile;
    
    // getter, setter λ“±
}

@Entity
public class UserProfile {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String address;
    private String phoneNumber;
    
    @OneToOne(mappedBy = "userProfile") // 주인이 μ•„λ‹˜μ„ λͺ…μ‹œ
    private User user;
    
    // getter, setter λ“±
}
  • μœ„ μ½”λ“œμ—μ„œ User μ—”ν‹°ν‹°κ°€ UserProfile 엔티티와 μ—°κ΄€κ΄€κ³„μ—μ„œ μ£ΌμΈμž…λ‹ˆλ‹€.
    • userProfile ν•„λ“œμ— λŒ€ν•œ μ„€μ •λ§Œ λ°μ΄ν„°λ² μ΄μŠ€μ— λ°˜μ˜λ©λ‹ˆλ‹€.

πŸ‘‰ 연관관계 주인 효과의 μ˜ˆμ‹œ 상황.

User user = new User();
UserProfile profile = new UserProfile();

user.setUserProfile(profile);
profile.setUser(user);

entityManager.persist(user);
entityManager.persist(profile);
  • μœ„μ™€ 같이 user의 userProfile을 μ„€μ •ν•˜κ³  profile의 userλ₯Ό 섀정해도, μ‹€μ œ λ°μ΄ν„°λ² μ΄μŠ€μ—λŠ” User μ—”ν‹°ν‹°μ˜ userProfile에 μ„€μ •λœ κ°’λ§Œ μ™Έλž˜ ν‚€λ‘œ λ°˜μ˜λ©λ‹ˆλ‹€.
    • UserProfile μ—”ν‹°ν‹°μ—μ„œ user ν•„λ“œμ˜ 변경은 λ°μ΄ν„°λ² μ΄μŠ€μ— λ°˜μ˜λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
      • 이λ₯Ό 톡해 λΆˆν•„μš”ν•œ 쿼리가 λ°œμƒν•˜λŠ” 것을 λ°©μ§€ν•˜κ³ , 데이터 무결성을 μœ μ§€ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

3️⃣ μš”μ•½.

  • 연관관계 주인만 λ°μ΄ν„°λ² μ΄μŠ€μ— μ™Έλž˜ ν‚€ 정보λ₯Ό λ°˜μ˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • 주인이 μ•„λ‹Œ μ—”ν‹°ν‹°μ—μ„œ μ„€μ •ν•œ μ—°κ΄€κ΄€κ³„λŠ” λ°μ΄ν„°λ² μ΄μŠ€μ— λ°˜μ˜λ˜μ§€ μ•ŠμœΌλ©°, 읽기 μ „μš©μœΌλ‘œ μ‚¬μš©λ©λ‹ˆλ‹€.
  • 이λ₯Ό 톡해 μ€‘λ³΅λœ μ™Έλž˜ ν‚€ μ €μž₯을 λ°©μ§€ν•˜κ³ , 데이터 무결성을 μœ μ§€ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

4️⃣ 연관관계 주인.

  • μ™Έλž˜ ν‚€(Foreign Key)λ₯Ό κ΄€λ¦¬ν•˜λŠ” μ—”ν‹°ν‹°λ₯Ό λ§ν•©λ‹ˆλ‹€.
    • μ΄λŠ” λ°μ΄ν„°λ² μ΄μŠ€μ— 연관관계와 κ΄€λ ¨λœ 변경사항을 λ°˜μ˜ν•  λ•Œ μ–΄λ–€ μ—”ν‹°ν‹°κ°€ μ™Έλž˜ ν‚€μ˜ μˆ˜μ •, μ‚½μž…, μ‚­μ œ μž‘μ—…μ„ μˆ˜ν–‰ν•˜λŠ”μ§€λ₯Ό κ²°μ •ν•©λ‹ˆλ‹€.

5️⃣ 연관관계 주인의 μ—­ν• .

  • μ—°κ΄€κ΄€κ³„μ—μ„œ 주인으둜 μ§€μ •λœ μ—”ν‹°ν‹°λŠ” λ°μ΄ν„°λ² μ΄μŠ€μ˜ μ™Έλž˜ ν‚€ μ»¬λŸΌμ„ κ΄€λ¦¬ν•˜λ©°, 주인이 μ•„λ‹Œ μͺ½μ—μ„œλŠ” 연관관계λ₯Ό 읽기 μ „μš©μœΌλ‘œ μ‚¬μš©ν•©λ‹ˆλ‹€.
    • 예λ₯Ό λ“€μ–΄, μ–‘λ°©ν–₯ μ—°κ΄€κ΄€κ³„μ—μ„œ @OneToOne, @OneToMany, @ManyToMany, @ManyToOne, @ManyToMany 쀑 주인이 λ˜λŠ” μͺ½μ—μ„œλ§Œ μ™Έλž˜ ν‚€μ˜ 값이 λ³€κ²½λ©λ‹ˆλ‹€.

6️⃣ 연관관계 주인 μ„€μ •μ˜ μ€‘μš”μ„±.

  • JPAμ—μ„œ μ—°κ΄€κ΄€κ³„μ˜ 주인을 μ„€μ •ν•˜λŠ” μ΄μœ λŠ” μ–‘λ°©ν–₯ μ—°κ΄€κ΄€κ³„μ—μ„œ 두 μ—”ν‹°ν‹° λͺ¨λ‘ 연관관계λ₯Ό λ§Ίκ³  μžˆλŠ” 경우, μ–΄λŠ μͺ½μ΄ μ‹€μ œλ‘œ λ°μ΄ν„°λ² μ΄μŠ€μ— λ°˜μ˜ν• μ§€λ₯Ό λͺ…ν™•νžˆ ν•˜κΈ° μœ„ν•¨μž…λ‹ˆλ‹€.
    • 주인이 μ•„λ‹Œ μ—”ν‹°ν‹°μ—μ„œ 연관관계λ₯Ό μ„€μ •ν•˜λ”λΌλ„ λ°μ΄ν„°λ² μ΄μŠ€μ—λŠ” λ°˜μ˜λ˜μ§€ μ•Šκ³ , μ£ΌμΈμ—μ„œ μ„€μ •λœ λ‚΄μš©λ§Œ λ°˜μ˜λ©λ‹ˆλ‹€.

πŸ‘‰ 예제: 1:1 κ΄€κ³„μ—μ„œμ˜ 연관관계 주인 μ„€μ •.

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    
    @OneToOne
    @JoinColumn(name = "user_profile_id") // μ™Έλž˜ ν‚€ 관리
    private UserProfile userProfile;
    
    // getter, setter λ“±
}

@Entity
public class UserProfile {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String address;
    private String phoneNumber;
    
    @OneToOne(mappedBy = "userProfile") // 연관관계 주인이 μ•„λ‹˜μ„ λͺ…μ‹œ
    private User user;
    
    // getter, setter λ“±
}
  • μœ„ μ½”λ“œμ—μ„œ User μ—”ν‹°ν‹°κ°€ μ—°κ΄€κ΄€κ³„μ˜ 주인이며, UserProfile은 mappedBy 속성을 톡해 연관관계 주인이 μ•„λ‹˜μ„ λͺ…μ‹œν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.
    • 이 경우 μ™Έλž˜ ν‚€λŠ” User μ—”ν‹°ν‹°μ˜ user_profile_id μ»¬λŸΌμ— μ €μž₯λ©λ‹ˆλ‹€.

7️⃣ μš”μ•½.

  • 연관관계 주인 : λ°μ΄ν„°λ² μ΄μŠ€μ— μ™Έλž˜ ν‚€λ₯Ό κ΄€λ¦¬ν•˜λŠ” μ—”ν‹°ν‹°. @JoinColumn을 톡해 섀정됨.
  • 주인이 μ•„λ‹Œ μ—”ν‹°ν‹° : mappedBy 속성을 톡해 μ„€μ •λ˜λ©°, 읽기 μ „μš©μœΌλ‘œ μž‘λ™.
  • 주인만이 μ™Έλž˜ ν‚€ μˆ˜μ •, μ‚­μ œ, μ‚½μž…μ„ κ΄€λ¦¬ν•˜κ³  λ°μ΄ν„°λ² μ΄μŠ€μ— λ°˜μ˜ν•¨.