TroubleShooting

[TroubleShooting] Cascade ์˜ต์…˜์€ ๋ถ€๋ชจ์—๊ฒŒ๋งŒ

soeun2537 2024. 10. 22. 15:39

๐Ÿ‘€ ํ˜„์žฌ ์ƒํ™ฉ ๋ฐ ๋ฐฐ๊ฒฝ ์„ค๋ช…

  • ํ˜„์žฌ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” N:M ๊ด€๊ณ„(๋‹ค๋Œ€๋‹ค ๊ด€๊ณ„)๊ฐ€ ์ ์šฉ๋œ Member์™€ Organization ์—”ํ‹ฐํ‹ฐ ๊ฐ„์˜ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ์„ค์ •ํ•˜๊ณ  ์žˆ๋‹ค. ์ค‘๊ฐ„ ํ…Œ์ด๋ธ”์ธ OrganizationMember ํ…Œ์ด๋ธ”์ด ๋‘ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์—ฐ๊ฒฐํ•ด์ฃผ๋ฉฐ, ์ด๋ฅผ ํ†ตํ•ด ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„๊ฐ€ ๊ด€๋ฆฌ๋œ๋‹ค. ์ค‘๊ฐ„ ํ…Œ์ด๋ธ”์„ ํ†ตํ•ด Member๋Š” ์—ฌ๋Ÿฌ Organization์— ์†ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋ฐ˜๋Œ€๋กœ ๊ฐ Organization๋„ ์—ฌ๋Ÿฌ Member์™€ ์—ฐ๊ด€๋  ์ˆ˜ ์žˆ๋‹ค. ํ˜„์žฌ๋Š” Organization ์—”ํ‹ฐํ‹ฐ์—์„œ๋งŒ ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„๋ฅผ ์„ค์ •ํ•˜์—ฌ ์ปฌ๋ ‰์…˜ ํ•„๋“œ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ๋‹ค.
  • ๋˜ํ•œ, CascadeType ์˜ต์…˜์„ ํ†ตํ•ด ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ ์‚ญ์ œ ์‹œ ์ž์‹ ์—”ํ‹ฐํ‹ฐ๋„ ์ž๋™์œผ๋กœ ์‚ญ์ œ๋˜๋„๋ก ๊ตฌํ˜„ํ–ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ค‘๊ฐ„ ํ…Œ์ด๋ธ”์ธ OrganizationMember๋ฅผ ํ†ตํ•ด ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„๋ฅผ ๊ด€๋ฆฌํ•  ๋•Œ, CascadeType ์˜ต์…˜์„ ์ž˜๋ชป ์„ค์ •ํ•˜๋ฉด ์˜๋„์น˜ ์•Š์€ ์‚ญ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.
 

โ–ถ ๊ตฌํ˜„ ์ฝ”๋“œ

๐Ÿ”ฝ Member ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค

@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    ...
}

๐Ÿ”ฝ Organization ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค

@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class Organization {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    ...

    // ์–‘๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„
    @Builder.Default
    @OneToMany(mappedBy = "organization", cascade = CascadeType.REMOVE, orphanRemoval = true)
    private List<OrganizationMember> organizationMembers = new ArrayList<>();
}

๐Ÿ”ฝ Organization๊ณผ Member์˜ ์ค‘๊ฐ„ ํ…Œ์ด๋ธ”์„ ๊ด€๋ฆฌํ•˜๋Š” OrganizationMember ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค

@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class OrganizationMember {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "organization_id")
    private Organization organization;

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "member_id")
    private Member member;

    ...
}
 

 

๐Ÿšจ ๋ฌธ์ œ ์ƒํ™ฉ

์ž์‹ ์—”ํ‹ฐํ‹ฐ์ธ OrganizationMember ์—”ํ‹ฐํ‹ฐ์—๋„ CascadeType.ALL์ด ์„ค์ •๋˜์–ด ์žˆ์–ด, ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ์ธ Organization์„ ์‚ญ์ œํ•  ๋•Œ ์˜ˆ์ƒํ•˜์ง€ ๋ชปํ•˜๊ฒŒ Member ์—”ํ‹ฐํ‹ฐ๊นŒ์ง€ ์‚ญ์ œ๋˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค. ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์ƒ์œผ๋กœ๋Š” ๋‹จ์ฒด๋ฅผ ์‚ญ์ œํ–ˆ์„ ๋ฟ์ธ๋ฐ, ํ•ด๋‹น ๋‹จ์ฒด์— ์†ํ•ด ์žˆ๋˜ ํšŒ์›๋“ค๊นŒ์ง€ ์‚ญ์ œ๋˜๋Š” ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•œ ๊ฒƒ์ด๋‹ค.

 

 

โœ๏ธ ์›์ธ ๋ถ„์„

  • Organization๊ณผ OrganizationMember๋Š” ๋ถ€๋ชจ-์ž์‹ ๊ด€๊ณ„๊ณ , OrganizationMember์™€ Member ์—ญ์‹œ ๋ถ€๋ชจ-์ž์‹ ๊ด€๊ณ„๋กœ ์„ค์ •๋˜์–ด ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ Organization์—์„œ OrganizationMember๋ฅผ ์‚ญ์ œํ•  ๋•Œ, OrganizationMember์— ์„ค์ •๋œ CascadeType ๋•Œ๋ฌธ์— Member๊นŒ์ง€ ์‚ญ์ œ๋˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค. ์ด๋Š” ์ž์‹ ์—”ํ‹ฐํ‹ฐ์— cascade ์˜ต์…˜์„ ์„ค์ •ํ•˜๋ฉด์„œ, ๋ถ€๋ชจ-์ž์‹ ๊ฐ„์˜ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ์ž˜๋ชป ๊ด€๋ฆฌํ•œ ๊ฒฐ๊ณผ๋‹ค.
  • ๋˜ํ•œ, ๋ถ€๋ชจ-์ž์‹ ๊ด€๊ณ„์™€ ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์— ๋Œ€ํ•œ ๊ฐœ๋…์ด ๋ช…ํ™•ํ•˜์ง€ ์•Š์•˜๋˜ ์ ๋„ ๋ฌธ์ œ์˜€๋‹ค. ํ˜„์žฌ ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์€ OrganizationMember์ธ๋ฐ, ์ด๋ฅผ ๋ถ€๋ชจ-์ž์‹ ๊ด€๊ณ„๋กœ ํ˜ผ๋™ํ•˜์—ฌ CascadeType์„ ์ ์šฉํ•ด์•ผ ํ•œ๋‹ค๊ณ  ์ž˜๋ชป ํŒ๋‹จํ•œ ๊ฒƒ์ด ์ด๋ฒˆ ์˜ค๋ฅ˜์˜ ์›์ธ ์ค‘ ํ•˜๋‚˜์˜€๋‹ค.
 

 

๐Ÿ”จ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ์—๋งŒ cascade ์˜ต์…˜์„ ์ ์šฉํ•˜๊ณ , ์ž์‹ ์—”ํ‹ฐํ‹ฐ์—๋Š” cascade๋ฅผ ์ ์šฉํ•˜์ง€ ์•Š๋„๋ก ์ˆ˜์ •ํ•œ๋‹ค. ์ž์‹ ์—”ํ‹ฐํ‹ฐ์—๋Š” ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ์™€์˜ ๊ด€๊ณ„๋งŒ์„ ์„ค์ •ํ•˜๊ณ , CascadeType์„ ์ œ๊ฑฐํ•œ๋‹ค.

๐Ÿ”ฝ ์ž์‹ ์—”ํ‹ฐํ‹ฐ์ธ OrganizationMember ํด๋ž˜์Šค

@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class OrganizationMember {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY) // CascadeType ์ œ๊ฑฐ
    @JoinColumn(name = "organization_id")
    private Organization organization;

    @ManyToOne(fetch = FetchType.LAZY) // CascadeType ์ œ๊ฑฐ
    @JoinColumn(name = "member_id")
    private Member member;

    ...
}

 

 

๐Ÿ” ๊ฒฐ๊ณผ ๊ด€์ฐฐ

Organization ์—”ํ‹ฐํ‹ฐ๋ฅผ ์‚ญ์ œํ•ด๋„ Member ์—”ํ‹ฐํ‹ฐ๋Š” ๊ทธ๋Œ€๋กœ ์œ ์ง€๋˜๋ฉฐ, OrganizationMember ํ…Œ์ด๋ธ”์˜ row๋งŒ ์‚ญ์ œ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ–ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์ƒ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์น˜๋ช…์ ์ธ ์˜ค๋ฅ˜๋ฅผ ๋ฐฉ์ง€ํ•˜๊ณ , ๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ์„ ์„ฑ๊ณต์ ์œผ๋กœ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

 

 

๐Ÿ’ก ๊ณ ์ฐฐ

์—ฐ๊ด€๊ด€๊ณ„์—์„œ CascadeType์€ ํšจ์œจ์ ์ธ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜์ง€๋งŒ, ์ž์นซ ์ž˜๋ชป ์„ค์ •ํ•˜๋ฉด ์˜๋„ํ•˜์ง€ ์•Š์€ ์—”ํ‹ฐํ‹ฐ ์‚ญ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค. ํŠนํžˆ ๋ถ€๋ชจ-์ž์‹ ๊ด€๊ณ„์—์„œ CascadeType์„ ์ ์šฉํ•  ๋•Œ, ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ๊ณผ ๋ฐฉํ–ฅ์„ฑ์„ ๋ช…ํ™•ํžˆ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๋‹ค. ๋ถ€๋ชจ์™€ ์ž์‹ ๊ด€๊ณ„๋ฅผ ํ—ท๊ฐˆ๋ ค ์ž์‹ ์—”ํ‹ฐํ‹ฐ์— cascade๋ฅผ ์ ์šฉํ•  ๊ฒฝ์šฐ, ์ž์‹์ด ์‚ญ์ œ๋  ๋•Œ ๋ถ€๋ชจ๋„ ํ•จ๊ป˜ ์‚ญ์ œ๋˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋กœ ์ธํ•ด ๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ์— ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๊ณ , ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์—๋„ ์น˜๋ช…์ ์ธ ์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฒˆ ๋ฌธ์ œ๋ฅผ ํ†ตํ•ด CascadeType์˜ ์‚ฌ์šฉ๊ณผ ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ ๊ฐœ๋…์— ๋Œ€ํ•ด ๊นŠ์€ ์ดํ•ด๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋Š” ๊ฒƒ์„ ๊นจ๋‹ฌ์•˜์œผ๋ฉฐ, ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ์—์„œ๋งŒ cascade๋ฅผ ์‹ ์ค‘ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ์„ ๋ณด์žฅํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ž„์„ ์•Œ๊ฒŒ ๋˜์—ˆ๋‹ค.

 
 

๐Ÿ“ ์ฐธ๊ณ  ์ž๋ฃŒ