๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐ŸŒฟ Spring/Spring Data JPA

[JPA] ์˜์†ํ™” ์ˆœ์„œ ๋ณ€๊ฒฝ - Dirty Checking๊ณผ @DynamicUpdate ์‚ฌ์šฉ

by Lucy Oh 2024. 6. 7.
Dirty Checking๊ณผ ๊ด€๋ จ๋œ ๋‚ด์šฉ์„ ๋ณต์Šตํ•˜๋ฉฐ ์˜์†ํ™” ์ˆœ์„œ์— ๋Œ€ํ•ด ๊ถ๊ธˆํ–ˆ๋˜ ๊ฒƒ๋“ค, ๊ทธ๋ฆฌ๊ณ  ์‹คํ—˜ํ–ˆ๋˜ ๊ธฐ๋ก์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

 

๊ฐ•์˜ ๋‚ด์šฉ

Parent Entity์™€ Child Entity๊ฐ€ 1:N์œผ๋กœ ์—ฐ๊ด€๊ด€๊ณ„๊ฐ€ ๋งบ์–ด์ ธ ์žˆ์Œ. (๋”ฐ๋ผ์„œ ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์€ Child Entity.)

์ด ์ƒํƒœ์—์„œ ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰์‹œํ‚ฌ ๊ฒฝ์šฐ, 

Child child1 = new Child();
child1.setName("child1");

Child child2 = new Child();
child2.setName("child1");

Parent parent = new Parent();
parent.setName("Parent1");
parent.addChild(child1);
parent.addChild(child2);

em.persist(parent);
em.persist(child1);
em.persist(child2);

 

 

์˜ค๋ฅธ์ชฝ ์‚ฌ์ง„ ์ฐธ๊ณ  โ–ถ

  1. Parent Entity INSERT
  2. (child1) Child Entity INSERT
  3. (child2) Child Entity INSERT

์ด 3๋ฒˆ์˜ INSERT ์ฟผ๋ฆฌ๊ฐ€ ๋‚˜๊ฐ€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

 

 

 

 

 

 

 

 

 

 


1) ๋งŒ์•ฝ Child์™€ Parent์˜ ์˜์†ํ™” ์ˆœ์„œ๋ฅผ ๋’ค์ง‘์œผ๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ?

๊ฐ€์ •

์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ(FK๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์ชฝ - ์—ฌ๊ธฐ์„œ๋Š” Child)์„ ๊ฐ€์žฅ ๋จผ์ € ์˜์†ํ™” ์‹œ์ผœ์ฃผ๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ณด์ž

  • Parent๋ฅผ ์˜์†ํ™”ํ•˜๊ธฐ ์ „, Child๋ฅผ ๋จผ์ € ์˜์†ํ™”ํ•˜์˜€๊ณ , ์ดํ›„์— Parent๋ฅผ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ๋“ฑ๋กํ•จ.
Child child1 = new Child();
child1.setName("child1");

Child child2 = new Child();
child2.setName("child1");

Parent parent = new Parent();
parent.setName("Parent1");
parent.addChild(child1);
parent.addChild(child2);

em.persist(child1); // child๋ฅผ ๋จผ์ € ์˜์†ํ™”
em.persist(child2);
em.persist(parent);

System.out.println("=========");

 

๊ฒฐ๊ณผ

์ด ๊ฒฝ์šฐ, ์ฟผ๋ฆฌ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋‚˜๊ฐ€๊ฒŒ ๋œ๋‹ค. โ–ถ

  1. INSERT CHILD1
  2. INSERT CHILD2
  3. INSERT PARENT
  4. UPDATE CHILD1
  5. UPDATE CHILD2

๊ทธ๋ ‡๋‹ค๋ฉด ์—ฌ๊ธฐ์„œ ์™œ CHILD์˜ ์ปฌ๋Ÿผ์„ ์ „๋ถ€ ์—…๋ฐ์ดํŠธํ•˜๋Š” ์ฟผ๋ฆฌ๊ฐ€ ๋“ฑ์žฅํ•˜์˜€์„๊นŒ?

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

์ด์œ  [ Dirty Checking ]

"์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์™€ ๋ณ€๊ฒฝ ๊ฐ์ง€" ๋ณต์Šต

์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—” 1์ฐจ ์บ์‹œ๊ฐ€ ์žˆ๊ณ , ์ด ์•ˆ์—” ID, Entity, ์Šค๋ƒ…์ƒท์ด ์ €์žฅ๋˜์–ด ์žˆ์Œ.
์ด ๋•Œ, ์Šค๋ƒ…์ƒท์€ DB์—์„œ ๊ฐ’์„ ์ฝ์–ด์™”์„ ๋•Œ (์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ๋“ค์–ด์™”์„ ๋•Œ)์˜ ์ƒํƒœ๋ฅผ ์ €์žฅํ•ด๋‘” ๊ฒƒ

 

persist() ํ•  ๋•Œ (์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ๋“ฑ๋กํ•  ๋•Œ) Child์— ์—ฐ๊ด€๋œ Parent๋Š” ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—๋„ ๋“ฑ๋ก์ด ๋˜์–ด ์žˆ์ง€ ์•Š๊ณ , DB์—๋„ ๋“ฑ๋ก๋˜์–ด ์žˆ์ง€ ์•Š์Œ.

๋”ฐ๋ผ์„œ Child ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ๋“ฑ๋ก๋  ์‹œ์ ์—” PARENT_ID (FK)๊ฐ€ NULL๋กœ ์„ค์ •๋œ ๋ฒ„์ „์ด ์Šค๋ƒ…์ƒท์œผ๋กœ ์ €์žฅ๋˜๋Š” ๊ฒƒ!

 

์ดํ›„์— Parent๋ฅผ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ๋“ฑ๋กํ•œ ํ›„์—์•ผ Child์˜ FK๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๊ธฐ์—,
ํŠธ๋žœ์žญ์…˜์˜ ์ปค๋ฐ‹ ์‹œ์ ์— Dirty Checking์„ ํ†ตํ•ด UPDATE ์ฟผ๋ฆฌ๊ฐ€ ์ƒ์„ฑ๋˜๋ฉด ์ด ๋•Œ PARENT_ID, ์ฆ‰ FK๊ฐ€ UPDATE ๋˜๋Š” ๊ฒƒ.

  • Dirty Checking ๊ณผ์ •์„ ํ†ตํ•ด FK๊ฐ€ ์„ธํŒ…๋œ ์—”ํ‹ฐํ‹ฐ์™€ ์•„์ง FK๊ฐ€ ์„ธํŒ…๋˜์ง€ ์•Š์€ ์Šค๋ƒ…์ƒท์„ ๋น„๊ตํ•˜๋ฉด์„œ UPDATE ์ฟผ๋ฆฌ๋ฅผ ๋‚ ๋ ค์ค€ ๊ฒƒ.

2) ํ•˜๋‚˜์˜ ์—”ํ‹ฐํ‹ฐ์— ๋‘ ๋ฒˆ์˜ ์ˆ˜์ •์‚ฌํ•ญ์ด ์ƒ๊ธด๋‹ค๋ฉด, UPDATE๋Š” ๋‘ ๋ฒˆ ๋‚˜๊ฐˆ๊นŒ?

๊ฐ€์ •

Child๋ฅผ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ๋“ฑ๋ก์‹œํ‚จ ์ดํ›„์— FK๋„ ๋ฐ”๊พธ๊ณ , ์ด๋ฆ„๋„ ๋ฐ”๊ฟ”์ค€๋‹ค๋ฉด UPDATE ์ฟผ๋ฆฌ๋Š” ๋‘ ๋ฒˆ ๋‚˜๊ฐˆ๊นŒ?

  1. Parent๋ฅผ Child๋ณด๋‹ค ์ดํ›„์— ๋“ฑ๋ก ⇒ FK UPDATE
  2. child1์˜ ์ด๋ฆ„์„ ๋ณ€๊ฒฝ
Child child1 = new Child();
child1.setName("child1");

Child child2 = new Child();
child2.setName("child1");

Parent parent = new Parent();
parent.setName("Parent1");
parent.addChild(child1);
parent.addChild(child2);

em.persist(child1);
em.persist(child2);
em.persist(parent);

child1.setName("childA");

System.out.println("=========");

 

๊ฒฐ๊ณผ

Child1์— ๋Œ€ํ•œ UPDATE์ฟผ๋ฆฌ๋Š” ํ•œ ๋ฒˆ๋งŒ ๋‚˜๊ฐ€๊ฒŒ ๋จ!

 

์—ฌ๊ธฐ์„œ UPDATE ์ฟผ๋ฆฌ๊ฐ€ CHILD1๊ณผ CHILD2 ๋ชจ๋‘ name๊ณผ PARENT_ID๋ฅผ ๋ชจ๋‘ ์—…๋ฐ์ดํŠธ ์‹œํ‚ค๊ณ  ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ํ™•์ธํ•ด์•ผ ํ•จ.

  • CHILD1์€ name, PARENT_ID ๋ชจ๋‘ UPDATEํ•˜๊ณ  ์žˆ์ง€๋งŒ,
  • CHILD2๋Š” PARENT_ID๋งŒ ๋ฐ”๋€ ์ƒํ™ฉ์ž„์—๋„ name๊นŒ์ง€ ๋ชจ๋‘ ์—…๋ฐ์ดํŠธ ๋˜๊ณ  ์žˆ์Œ.

 

์ฆ‰, ํ•œ ์—”ํ‹ฐํ‹ฐ์˜ ๋ ˆ์ฝ”๋“œ ์ค‘ ๋ฌด์—‡์ด ๋ณ€๊ฒฝ๋˜์—ˆ๋Š”์ง€๋ฅผ ์ผ์ผํžˆ ํ™•์ธํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ์ด ์•„๋‹Œ, ์—”ํ‹ฐํ‹ฐ์˜ ๋ชจ๋“  ๋ ˆ์ฝ”๋“œ๋ฅผ ์—…๋ฐ์ดํŠธ ํ•˜๊ณ  ์žˆ๋‹ค๋Š” ์ !

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

์ด์œ  [@DynamicUpdate]

ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๋Š” ์—”ํ‹ฐํ‹ฐ์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๊ฐ์ง€ํ•  ๋•Œ, ๋ณ€๊ฒฝ๋œ ํ•„๋“œ๋งŒ์„ ๋Œ€์ƒ์œผ๋กœ ํ•˜๋Š” '๋ถ€๋ถ„ ์—…๋ฐ์ดํŠธ'๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š๊ณ , ์—”ํ‹ฐํ‹ฐ์˜ ๋ชจ๋“  ํ•„๋“œ๋ฅผ ํฌํ•จํ•˜๋Š” '์ „์ฒด ์—…๋ฐ์ดํŠธ'๋ฅผ ์‹คํ–‰ํ•จ.

 

@DynamicUpdate ์• ๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๊ฐ€ ๋ณ€๊ฒฝ๋œ ํ•„๋“œ์— ๋Œ€ํ•ด์„œ๋งŒ ์—…๋ฐ์ดํŠธ SQL์„ ์ƒ์„ฑํ•˜๋„๋ก ํ•  ์ˆ˜ ์žˆ์œผ๋‚˜, ์‹ ์ค‘ํ•˜๊ฒŒ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋ฉฐ ๋ชจ๋“  ์ƒํ™ฉ์—์„œ ์„ฑ๋Šฅ ๊ฐœ์„ ์„ ๋ณด์žฅํ•˜์ง€๋Š” ์•Š์Œ.

์ถœ์ฒ˜: ์ธํ”„๋Ÿฐ ์งˆ๋ฌธ

3) @DynamicUpdate๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด UPDATE ์ฟผ๋ฆฌ๋Š” ์–ด๋–ป๊ฒŒ ๋ณ€ํ• ๊นŒ?

๊ฐ€์ •

๋‹ค์Œ๊ณผ ๊ฐ™์ด Child์— @DynamicUpdate๋ฅผ ์ถ”๊ฐ€ํ•œ ๋’ค, 2๋ฒˆ์˜ ์ˆ˜์ •์‚ฌํ•ญ์ด ๋ฐœ์ƒํ•˜๋ฉด ์–ด๋–ค UPDATE ์ฟผ๋ฆฌ๊ฐ€ ๋‚˜๊ฐ€๊ฒŒ ๋ ๊นŒ?

@Entity
@DynamicUpdate // ์• ๋…ธํ…Œ์ด์…˜ ์ถ”๊ฐ€
public class Child {

    @Id
    @GeneratedValue
    private Long id;
    
    
    ...
    
}

 

๊ฒฐ๊ณผ

CHILD1

  • name๊ณผ PARENT_ID๊ฐ€ ์ „๋ถ€ ๋ณ€๊ฒฝ๋˜์—ˆ์œผ๋ฏ€๋กœ, name๊ณผ PARENT_ID๊ฐ€ ์ „๋ถ€ UPDATE ๋˜๋Š” ์ฟผ๋ฆฌ๊ฐ€ ์ƒ์„ฑ๋จ.
Hibernate: 
    /* update
        for hellojpa.Child */update Child 
    set
        name=?,
        PARENT_ID=? 
    where
        id=?

 

 

CHILD2

  • FK(PARENT_ID)๋งŒ ๋ณ€๊ฒฝ๋˜์—ˆ์œผ๋ฏ€๋กœ, PARENT_ID๋งŒ UPDATE ๋˜๋Š” ์ฟผ๋ฆฌ๊ฐ€ ์ƒ์„ฑ๋จ.
Hibernate: 
    /* update
        for hellojpa.Child */update Child 
    set
        PARENT_ID=? 
    where
        id=?

 

 

 

 

 


@DynamicUpdate๋ฅผ ์‹ ์ค‘ํ•˜๊ฒŒ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ์ด์œ 

PreparedStatement์˜ ํŠน์ง•

๋ฏธ๋ฆฌ ์ปดํŒŒ์ผ๋œ SQL ๋ฌธ์„ ํฌํ•จํ•˜๊ณ  ์žˆ์–ด PreparedStatment๊ฐ€ ์‹คํ–‰๋  ๋•Œ DBMS๊ฐ€ ๋จผ์ € ์ปดํŒŒ์ผํ•  ํ•„์š” ์—†์ด ๋ฐ”๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•จ.

  • ์—ฌ๊ธฐ์„œ ?๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š”๋ฐ, ๋™์ ์œผ๋กœ ๊ฐ’์„ ๋„ฃ์„ ์ˆ˜ ์žˆ์Œ.

 

ex)

String updateString =
  "update COFFEES set SALES = ? where COF_NAME = ?";


PreparedStatement updateSales = con.prepareStatement(updateString);

updateSales.setInt(1, 80);
updateSales.setString(2, "MAXIM");
updateSales.executeUpdate();

 

ํ•ญ์ƒ ์„ฑ๋Šฅ์— ์œ ๋ฆฌํ•˜์ง€๋Š” ์•Š๋‹ค!

JPA๋Š” ์œ„์™€ ๊ฐ™์ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ๋”ฉ ์‹œ์ ์— PreparedStatement ์Šคํƒ€์ผ๋กœ ํ•ด๋‹น ์—”ํ‹ฐํ‹ฐ์˜ UPDATE ์ฟผ๋ฆฌ๋ฅผ ๋งŒ๋“ฆ.

 

๋”ฐ๋ผ์„œ ์„ฑ๋Šฅ๋งŒ ์ƒ๊ฐํ•˜๋ฉด

  1. ํ•ด๋‹น PreparedStatement์˜ ์ปฌ๋Ÿผ์— ๊ฐ’์„ ์ „๋ถ€ ๋„˜๊ธฐ๋Š” ๊ฒƒ๊ณผ
  2. @DynamicInsert๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ•˜๋‚˜๋งŒ UPDATE๋ฅผ ํ•˜๋Š” ๊ฒƒ

์œ„ ๋‘๊ฐ€์ง€๊ฐ€ ํฐ ์ฐจ์ด๊ฐ€ ์—†์Œ.

 

์˜คํžˆ๋ ค 2๋ฒˆ ๋ณด๋‹ค 1๋ฒˆ์ฒ˜๋Ÿผ ์ „์ฒด ์ปฌ๋Ÿผ์„ PreparedStatement ์Šคํƒ€์ผ์˜ SQL์„ ๋ฐ˜๋ณตํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ๋” ์†๋„๊ฐ€ ๋น ๋ฅผ ์ˆ˜๋„ ์žˆ๋Š” ๊ฒƒ!

(๋ฌผ๋ก  ์ปฌ๋Ÿผ์ด ๋„ˆ๋ฌด ๋งŽ๊ฑฐ๋‚˜, ๊ธธ์ด๊ฐ€ ๋„ˆ๋ฌด ๊ธธ๊ฑฐ๋‚˜, ๋ฐ์ดํ„ฐ๊ฐ€ ํฌ๋‹ค๋ฉด ์ƒํ™ฉ์€ ๋‹ฌ๋ผ์ง.)

 

์ถœ์ฒ˜
[Oracle docs] JDBC Basics - Using Prepared Statement
์ธํ”„๋Ÿฐ ์งˆ๋ฌธ - "์—…๋ฐ์ดํŠธ ๊ณ ๊ฒฌ ๊ตฌํ•ฉ๋‹ˆ๋‹ค."

 

 

๋Œ“๊ธ€