๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
Today I Learned(TIL)/์ˆ˜์ค€๋ณ„ ํ•™์Šต๋ฐ˜

์ˆ˜์ค€๋ณ„ ํ•™์Šต๋ฐ˜_๋ฒ ์ด์ง๋ฐ˜ 5ํšŒ์ฐจ ์„ธ์…˜

by carrot0911 2024. 12. 22.

๋ฒ ์ด์ง๋ฐ˜

๋ฐ์ดํ„ฐ๋ชจ๋ธ๋ง๊ณผ ์—ฐ๊ด€๊ด€๊ณ„

12/19 11:00 ~ 12:00 (์•ฝ 1์‹œ๊ฐ„ ์ง„ํ–‰)

 

์ค‘์š”ํ‚ค์›Œ๋“œ

  • ๋‹จ๋ฐฉํ–ฅ vs ์–‘๋ฐฉํ–ฅ
  • ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ
  • ๋ถ€๋ชจ-์ž์‹ ๊ด€๊ณ„
  • cascade
  • orphanRemoval

 

๋ฐ์ดํ„ฐ ๋ชจ๋ธ๋ง

1. ๋‚ด์ผ๋ฐฐ์›€์บ ํ”„ ๋น„์ฆˆ๋‹ˆ์Šค ์š”๊ตฌ์‚ฌํ•ญ ๋ถ„์„

a. ์‹œ๋‚˜๋ฆฌ์˜ค์— ์•Œ๋งž์€ ๋น„์ฆˆ๋‹ˆ์Šค ๊ด€์ ์€?!

๋‚ด์ผ๋ฐฐ์›€์บ ํ”„ ๋น„์ฆˆ๋‹ˆ์Šค ๊ด€์ 

b. ๋‚ด์ผ๋ฐฐ์›€์บ ํ”„ ์‹œ์Šคํ…œ์€ ______________________์ด๋‹ค!

  • ํ•œ ๋ช…์˜ ํ•™์ƒ์ด ํ•˜๋‚˜์˜ ์ˆ˜์—…๋งŒ ๋“ค์„ ์ˆ˜ ์žˆ๋Š” ๊ตฌ์กฐ
  • ํ•˜๋‚˜์˜ ์ˆ˜์—…์ด ์—ฌ๋Ÿฌ ๋ช…์˜ ํ•™์ƒ์„ ํฌํ•จํ•  ์ˆ˜ ์žˆ๋Š” ๊ตฌ์กฐ

2. ํ…Œ์ด๋ธ” ์ง์ ‘ ๊ทธ๋ ค๋ณด๊ธฐ

a. case1: Course ํ…Œ์ด๋ธ”์ด studentId๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ฒฝ์šฐ

student ํ…Œ์ด๋ธ”

pk (ํ•™์ƒ) name
1 gygim
2 Steve

Course ํ…Œ์ด๋ธ”

pk (์ˆ˜์—…) student_id name
1 1 Spring
2 1 Ktor
3 1 React
4 2 Java
5 2 Kotlin
  • ํ•œ ๋ช…์˜ ํ•™์ƒ์ด ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ˆ˜์—…์„ ๋“ค์„ ์ˆ˜ ์žˆ๋Š” ๊ตฌ์กฐ
  • ํ•˜๋‚˜์˜ ์ˆ˜์—…์ด ํ•˜๋‚˜์˜ ํ•™์ƒ๋งŒ ํฌํ•จํ•  ์ˆ˜ ์žˆ๋Š” ๊ตฌ์กฐ

โ–ถ ๋‚ด์ผ๋ฐฐ์›€์บ ํ”„์˜ ์š”๊ตฌ์‚ฌํ•ญ๊ณผ ๋งž์ง€ ์•Š๋‹ค.

b. case2: Student ํ…Œ์ด๋ธ”์ด courseId๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ฒฝ์šฐ

Course ํ…Œ์ด๋ธ”

pk (์ˆ˜์—…) name
1 Spring
2 Java

Student ํ…Œ์ด๋ธ”

pk (ํ•™์ƒ) course_id name
1 1 gygim
2 1 Steve
3 1 John
4 2 Isac
5 2 David
  • ํ•œ ๋ช…์˜ ํ•™์ƒ์ด ํ•˜๋‚˜์˜ ์ˆ˜์—…๋งŒ ๋“ค์„ ์ˆ˜ ์žˆ๋Š” ๊ตฌ์กฐ
  • ํ•˜๋‚˜์˜ ์ˆ˜์—…์ด ์—ฌ๋Ÿฌ ๋ช…์˜ ํ•™์ƒ์„ ํฌํ•จํ•  ์ˆ˜ ์žˆ๋Š” ๊ตฌ์กฐ

โ–ถ ๋‚ด์ผ๋ฐฐ์›€์บ ํ”„์˜ ์š”๊ตฌ์‚ฌํ•ญ๊ณผ ๋™์ผํ•˜๋‹ค.

 

๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ์˜ ์—ฐ๊ด€๊ด€๊ณ„ ๊ฐœ๋…

  • ๋‹จ๋ฐฉํ–ฅ vs ์–‘๋ฐฉํ–ฅ
  • ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ
  • ๋ถ€๋ชจ - ์ž์‹ ๊ด€๊ณ„

1. ๋‹จ๋ฐฉํ–ฅ vs ์–‘๋ฐฉํ–ฅ

a. ๋‹จ๋ฐฉํ–ฅ: ํ•œ์ชฝ์—์„œ๋งŒ ๋‹ค๋ฅธ ์—”ํ‹ฐํ‹ฐ(Entity)๋ฅผ ์ฐธ์กฐํ•˜๋Š” ์ƒํ™ฉ
@ManyToOne
๋งŒ ์„ค์ •ํ•œ ์ƒํ™ฉ

@Entity
public class Student {

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

    private String name;

    @ManyToOne
    @JoinColumn(name = "course_id") // ์™ธ๋ž˜ ํ‚ค ๊ด€๋ฆฌ
    private Course course; // ๋‹จ๋ฐฉํ–ฅ ๊ด€๊ณ„
}

b. ์–‘๋ฐฉํ–ฅ: ์—”ํ‹ฐํ‹ฐ(Entity)๊ฐ€ ์„œ๋กœ ์ฐธ์กฐํ•˜๋Š” ์ƒํ™ฉ
@OneToMany
๋ฅผ ์ถ”๊ฐ€๋กœ ์„ค์ •ํ•œ ์ƒํ™ฉ

@Entity
public class Course {

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

    private String name;

    @OneToMany(mappedBy = "course")
    private List<Student> students = new ArrayList<>();
}

@Entity
public class Student {

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

    private String name;

    @ManyToOne
    @JoinColumn(name = "course_id")
    private Course course;
}

2. ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ (๋ฐ์ดํ„ฐ ๊ด€์ )

๋ฐ์ดํ„ฐ ๊ด€์ ์—์„œ ๋ฌผ๋ฆฌ์ ์œผ๋กœ ์™ธ๋ž˜ํ‚ค(FK: ForeignKey)๋ฅผ ์†Œ์œ ํ•˜๊ณ  ์žˆ๋Š” ์—”ํ‹ฐํ‹ฐ(Entity)(ํ…Œ์ด๋ธ”)์ด๋‹ค.

PK FK name
์‹๋ณ„์ž ์™ธ๋ž˜ํ‚ค ์†์„ฑ
class Entity {
    private Long id;       // ์‹๋ณ„์ž
    private Entity entity; // ์™ธ๋ž˜ํ‚ค
    private String name;   // ์†์„ฑ
}

3. ๋ถ€๋ชจ - ์ž์‹ ๊ด€๊ณ„ (๋น„์ฆˆ๋‹ˆ์Šค ๊ด€์ )

๋น„์ฆˆ๋‹ˆ์Šค ๊ด€์ ์—์„œ ํ•œ ์—”ํ‹ฐํ‹ฐ(Entity)๊ฐ€ ๋‹ค๋ฅธ ์—”ํ‹ฐํ‹ฐ(Entity)๋ฅผ ํฌํ•จํ•˜๋Š” ๋…ผ๋ฆฌ์  ๊ด€๊ณ„๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

4. ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ ํ™œ์šฉ๋˜๋Š” ๋ฐ์ดํ„ฐ ๊ด€๊ณ„ ์‚ดํŽด๋ณด๊ธฐ

a. ํ•™์ƒ์˜ ์—ฐ๊ด€๊ด€๊ณ„ ์‚ดํŽด๋ณด๊ธฐ

๋ฐ์ดํ„ฐ ๊ด€์ 

id course_id name
1 2 gygim
public class Student {
    ...
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "course_id")
    private Course course;

    private String name;

    ...
}

๋น„์ฆˆ๋‹ˆ์Šค ๊ด€์ 

  • ํ•™์ƒ์€ ์—ฐ๊ด€ ๊ด€๊ณ„์—์„œ ์ฃผ์ธ์ด๋‹ค.
  • ํ•™์ƒ์€ ๋ถ€๋ชจ - ์ž์‹ ๊ด€๊ณ„์—์„œ ์ž์‹์ด๋‹ค.

b. ์ˆ˜์—…์˜ ์—ฐ๊ด€ ๊ด€๊ณ„ ์‚ดํŽด๋ณด๊ธฐ

๋ฐ์ดํ„ฐ ๊ด€์ 

id name
1 Spring
public class Course {

    ...

    @OneToMany(mappedBy = "course", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Student> students;

    ...
}

๋น„์ฆˆ๋‹ˆ์Šค ๊ด€์ 

  • ์ˆ˜์—…์€ ์—ฐ๊ด€ ๊ด€๊ณ„์—์„œ ์ฃผ์ธ์ด ์•„๋‹ˆ๋‹ค.
  • ์ˆ˜์—…์€ ๋ถ€๋ชจ - ์ž์‹ ๊ด€๊ณ„์—์„œ ๋ถ€๋ชจ์ด๋‹ค.

5. ์ •๋ฆฌ

student_id course_id name
1 2 gygim
public class Student {
    ...
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "course_id")
    private Course course;

    private String name;

    ...
}
course_id name
1 Spring
public class Course {
    ...

    @OneToMany(mappedBy = "course", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Student> students;

    ...
}
  ์—ฐ๊ด€ ๊ด€๊ณ„ ์ฃผ์ธ ๋ถ€๋ชจ ์ž์‹
ํ•™์ƒ โœ… โŒ โœ…
์ˆ˜์—… โŒ โœ… โŒ
  • @JoinColumn์€ ํ˜„์žฌ ์—”ํ‹ฐํ‹ฐ(Entity)๊ฐ€ ์†Œ์œ ํ•˜๊ณ  ์žˆ๋Š” ์™ธ๋ž˜ํ‚ค ์ปฌ๋Ÿผ์ด ์–ด๋–ค ์†์„ฑ๊ณผ ์—ฐ๊ฒฐ๋˜๋Š”์ง€ ๋ช…์‹œํ•œ๋‹ค.
  • @JoinColumn์ด ์žˆ๋Š” ์ชฝ์ด ์—ฐ๊ด€ ๊ด€๊ณ„ ์ฃผ์ธ์ด๋‹ค.

 

์ถ”๊ฐ€: CascadeType๊ณผ orphanRemoval

1. CascadeType

๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ(Entity)๊ฐ€ ์ˆ˜ํ–‰ํ•˜๋Š” ์ž‘์—…(์˜์†ํ™”, ๋ณ‘ํ•ฉ, ์‚ญ์ œ ๋“ฑ)์„ ์ž์‹ ์—”ํ‹ฐํ‹ฐ(Entity)์—๊ฒŒ ์ „ํŒŒํ•˜๋Š” ๊ธฐ์ค€์ด๋‹ค.

PERSIST ๋ถ€๋ชจ๋ฅผ ์ €์žฅํ•˜๋ฉด ์ž์‹๋„ ์ €์žฅ
MERGE ๋ถ€๋ชจ๋ฅผ ๋ณ‘ํ•ฉํ•˜๋ฉด ์ž์‹๋„ ๋ณ‘ํ•ฉ
REMOVE ๋ถ€๋ชจ๋ฅผ ์‚ญ์ œํ•˜๋ฉด ์ž์‹๋„ ์‚ญ์ œ
REFRESH ๋ถ€๋ชจ๋ฅผ ์ƒˆ๋กœ๊ณ ์นจํ•˜๋ฉด ์ž์‹๋„ ์ƒˆ๋กœ๊ณ ์นจ
DETACH ๋ถ€๋ชจ๋ฅผ ๋ถ„๋ฆฌํ•˜๋ฉด ์ž์‹๋„ ๋ถ„๋ฆฌ
ALL ์œ„์˜ ๋ชจ๋“  ์ž‘์—… ํฌํ•จ

2. CascadeType ํ™œ์šฉ ๋ฐฉ๋ฒ•

@Entity
public class Course {
    ...

    @OneToMany(cascade = CascadeType.ALL)
    @JoinColumn(name = "course_id") // ์™ธ๋ž˜ ํ‚ค ์„ค์ •
    private List<Student> students = new ArrayList<>();

    ...
}

3. orphanRemoval

๋ถ€๋ชจ์™€์˜ ๊ด€๊ณ„๊ฐ€ ๋Š์–ด์ง„(๋ฆฌ์ŠคํŠธ์—์„œ ์ œ๊ฑฐ๋œ) ์ž์‹ ์—”ํ‹ฐํ‹ฐ(Entity)๋ฅผ ์ž๋™์œผ๋กœ ์‚ญ์ œํ•˜๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.
๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ(Entity)๊ฐ€ ์‚ญ์ œ๋˜์ง€ ์•Š๋”๋ผ๋„ ์ž์‹ ์—”ํ‹ฐํ‹ฐ(Entity)๊ฐ€ ๋ฆฌ์ŠคํŠธ์—์„œ ์ œ๊ฑฐ๋˜๋ฉด ์ž์‹ ์—”ํ‹ฐํ‹ฐ(Entity)๋Š” ์‚ญ์ œ๋œ๋‹ค.