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);

์ค๋ฅธ์ชฝ ์ฌ์ง ์ฐธ๊ณ โถ
- Parent Entity INSERT
- (child1) Child Entity INSERT
- (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("=========");
๊ฒฐ๊ณผ

์ด ๊ฒฝ์ฐ, ์ฟผ๋ฆฌ๋ ๋ค์๊ณผ ๊ฐ์ด ๋๊ฐ๊ฒ ๋๋ค. โถ
- INSERT CHILD1
- INSERT CHILD2
- INSERT PARENT
- UPDATE CHILD1
- 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 ์ฟผ๋ฆฌ๋ ๋ ๋ฒ ๋๊ฐ๊น?
- Parent๋ฅผ Child๋ณด๋ค ์ดํ์ ๋ฑ๋ก โ FK UPDATE
- 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 ์ฟผ๋ฆฌ๋ฅผ ๋ง๋ฆ.
๋ฐ๋ผ์ ์ฑ๋ฅ๋ง ์๊ฐํ๋ฉด
- ํด๋น PreparedStatement์ ์ปฌ๋ผ์ ๊ฐ์ ์ ๋ถ ๋๊ธฐ๋ ๊ฒ๊ณผ
- @DynamicInsert๋ฅผ ์ฌ์ฉํ์ฌ ํ๋๋ง UPDATE๋ฅผ ํ๋ ๊ฒ
์ ๋๊ฐ์ง๊ฐ ํฐ ์ฐจ์ด๊ฐ ์์.
์คํ๋ ค 2๋ฒ ๋ณด๋ค 1๋ฒ์ฒ๋ผ ์ ์ฒด ์ปฌ๋ผ์ PreparedStatement ์คํ์ผ์ SQL์ ๋ฐ๋ณตํด์ ์ฌ์ฉํ๋ ๊ฒ ๋ ์๋๊ฐ ๋น ๋ฅผ ์๋ ์๋ ๊ฒ!
(๋ฌผ๋ก ์ปฌ๋ผ์ด ๋๋ฌด ๋ง๊ฑฐ๋, ๊ธธ์ด๊ฐ ๋๋ฌด ๊ธธ๊ฑฐ๋, ๋ฐ์ดํฐ๊ฐ ํฌ๋ค๋ฉด ์ํฉ์ ๋ฌ๋ผ์ง.)
์ถ์ฒ
[Oracle docs] JDBC Basics - Using Prepared Statement
์ธํ๋ฐ ์ง๋ฌธ - "์ ๋ฐ์ดํธ ๊ณ ๊ฒฌ ๊ตฌํฉ๋๋ค."
๋๊ธ