본문 바로가기
Java/JPA

[JPA_Basic] 값 타입과 불변 객체

by HJ0216 2023. 9. 14.

이 글은 김영한의 [자바 ORM 표준 JPA 프로그래밍 - 기본편] 수강하며 정리한 글입니다.

 

👉 기본 환경

- Language: Java

- DB: H2 Database

- IDE: IntelliJ

 

 

⌨️ 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class Main {
    public static void main(String[] args) {
 
        // 생략
 
        try {
 
            Address address = new Address("city""street""zipcode");
 
            MemberUsingEm memberUsingEm1 = new MemberUsingEm();
            memberUsingEm1.setUsername("hello1");
            memberUsingEm1.setHomeAddress(address);
            em.persist(memberUsingEm1);
 
            MemberUsingEm memberUsingEm2 = new MemberUsingEm();
            memberUsingEm2.setUsername("hello2");
            memberUsingEm2.setHomeAddress(address);
            em.persist(memberUsingEm2);
 
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
            e.printStackTrace();
        } finally {
            em.close();
        }
 
        emf.close();
 
    }
 
}
 
 

🖨️발생한 쿼리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Hibernate: 
    /* insert jpa_basic.MemberUsingEm
        */ insert 
        into
            MemberUsingEm
            (city, street, zipcode, username, WORK_CITY, WORK_STREET, WORK_ZIPCODE, endDate, startDate, id) 
        values
            (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
 
Hibernate: 
    /* insert jpa_basic.MemberUsingEm
        */ insert 
        into
            MemberUsingEm
            (city, street, zipcode, username, WORK_CITY, WORK_STREET, WORK_ZIPCODE, endDate, startDate, id) 
        values
            (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
 
 

 

 

🚨 memberUsingEm2의 city를 변경하고자 할 때, setter를 활용하면 side effect 발생

⌨️ 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class Main {
    public static void main(String[] args) {
 
        // 생략
 
        try {
 
            Address address = new Address("city""street""zipcode");
 
            MemberUsingEm memberUsingEm1 = new MemberUsingEm();
            memberUsingEm1.setUsername("hello1");
            memberUsingEm1.setHomeAddress(address);
            em.persist(memberUsingEm1);
 
            MemberUsingEm memberUsingEm2 = new MemberUsingEm();
            memberUsingEm2.setUsername("hello2");
            memberUsingEm2.setHomeAddress(address);
            em.persist(memberUsingEm2);
 
           memberUsingEm2.getHomeAddress().setCity("newCity");
 
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
            e.printStackTrace();
        } finally {
            em.close();
        }
 
        emf.close();
 
    }
 
}
 
 

🖨️발생한 쿼리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
Hibernate: 
    /* insert jpa_basic.MemberUsingEm
        */ insert 
        into
            MemberUsingEm
            (city, street, zipcode, username, WORK_CITY, WORK_STREET, WORK_ZIPCODE, endDate, startDate, id) 
        values
            (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
 
Hibernate: 
    /* insert jpa_basic.MemberUsingEm
        */ insert 
        into
            MemberUsingEm
            (city, street, zipcode, username, WORK_CITY, WORK_STREET, WORK_ZIPCODE, endDate, startDate, id) 
        values
            (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
 
Hibernate: 
    /* update
        jpa_basic.MemberUsingEm */ update
            MemberUsingEm 
        set
            city=?,
            street=?,
            zipcode=?,
            username=?,
            WORK_CITY=?,
            WORK_STREET=?,
            WORK_ZIPCODE=?,
            endDate=?,
            startDate=
        where
            id=?
 
Hibernate: 
    /* update
        jpa_basic.MemberUsingEm */ update
            MemberUsingEm 
        set
            city=?,
            street=?,
            zipcode=?,
            username=?,
            WORK_CITY=?,
            WORK_STREET=?,
            WORK_ZIPCODE=?,
            endDate=?,
            startDate=
        where
            id=?
 
 

memberUsingEm1의 city도 newCity로 update 발생

 

 

🤓 해결 방안: 새로운 Address 객체 생성

⌨️ 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class Main {
    public static void main(String[] args) {
 
        // 생략
 
        try {
 
            Address address = new Address("city""street""zipcode");
 
            MemberUsingEm memberUsingEm1 = new MemberUsingEm();
            memberUsingEm1.setUsername("hello1");
            memberUsingEm1.setHomeAddress(address);
            em.persist(memberUsingEm1);
 
            Address newAddress = new Address(address.getCity(), address.getStreet(), address.getZipcode());
 
            MemberUsingEm memberUsingEm2 = new MemberUsingEm();
            memberUsingEm2.setUsername("hello2");
            memberUsingEm2.setHomeAddress(newAddress);
            em.persist(memberUsingEm2);
 
            memberUsingEm2.getHomeAddress().setCity("newCity");
 
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
            e.printStackTrace();
        } finally {
            em.close();
        }
 
        emf.close();
 
    }
 
}
 
 

🖨️발생한 쿼리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Hibernate: 
    /* insert jpa_basic.MemberUsingEm
        */ insert 
        into
            MemberUsingEm
            (city, street, zipcode, username, WORK_CITY, WORK_STREET, WORK_ZIPCODE, endDate, startDate, id) 
        values
            (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
 
Hibernate: 
    /* insert jpa_basic.MemberUsingEm
        */ insert 
        into
            MemberUsingEm
            (city, street, zipcode, username, WORK_CITY, WORK_STREET, WORK_ZIPCODE, endDate, startDate, id) 
        values
            (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
 
Hibernate: 
    /* update
        jpa_basic.MemberUsingEm */ update
            MemberUsingEm 
        set
            city=?,
            street=?,
            zipcode=?,
            username=?,
            WORK_CITY=?,
            WORK_STREET=?,
            WORK_ZIPCODE=?,
            endDate=?,
            startDate=
        where
            id=?
 
 

 

 

⭐ 객체 타입을 수정할 수 없게 만들어 부작용을 차단

    ▶ 값 타입을 immutable object로 설계

    = 생성자로만 값을 설정하고, Setter를 만들지 않는 방법

 

* immutable object: 생성 시점 이후, 절대 값을 변경할 수 없는 객체

 

 

😮 값을 변경하고 싶을 경우에는 Setter가 아닌 새로운 Address 객체를 생성해야 함

⌨️ 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class Main {
    public static void main(String[] args) {
 
        // 생략
 
        try {
 
            Address address = new Address("city""street""zipcode");
 
            MemberUsingEm memberUsingEm1 = new MemberUsingEm();
            memberUsingEm1.setUsername("hello1");
            memberUsingEm1.setHomeAddress(address);
            em.persist(memberUsingEm1);
 
            Address newAddress = new Address("newCity", address.getStreet(), address.getZipcode());
            memberUsingEm1.setHomeAddress(newAddress);
 
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
            e.printStackTrace();
        } finally {
            em.close();
        }
 
        emf.close();
 
    }
 
}
 
 

🖨️발생한 쿼리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Hibernate: 
    /* insert jpa_basic.MemberUsingEm
        */ insert 
        into
            MemberUsingEm
            (city, street, zipcode, username, WORK_CITY, WORK_STREET, WORK_ZIPCODE, endDate, startDate, id) 
        values
            (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
 
Hibernate: 
    /* update
        jpa_basic.MemberUsingEm */ update
            MemberUsingEm 
        set
            city=?,
            street=?,
            zipcode=?,
            username=?,
            WORK_CITY=?,
            WORK_STREET=?,
            WORK_ZIPCODE=?,
            endDate=?,
            startDate=
        where
            id=?
 
 

 

 

🤓 em.persist(memberUsingEm1); 이후, 변경된 address를 persist 또는 update하지 않아도 DB에 반영되는 이유

em.persist(memberUsingEm1)를 호출하면 memberUsingEm1 엔티티가 영속성 컨텍스트에 등록

→ 이후 엔티티의 필드 값을 변경하면 JPA는 변경사항을 자동으로 감지

→ 트랜잭션을 커밋할 때, JPA는 영속성 컨텍스트에 있는 엔티티의 변경사항을 DB에 반영

 

* JPA의 더티 체킹(Dirty Checking)

영속성 컨텍스트가 엔티티의 원래 상태와 현재 상태를 비교하여 변경된 필드를 감지하고, 변경사항을 자동으로 DB에 반영

 

 

 

📚 참고 자료

 

[SpringBoot_JPA_1] Dirty Checking & Merge

이 글은 김영한의 [실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발]을 수강하며 정리한 글입니다. 🟦 기본 환경: IDE: IntelliJ, Language: Java 1. Dirty Checking, 변경감지 : Transaction Commit 시, 영속화

hj0216.tistory.com

 

'Java > JPA' 카테고리의 다른 글

[JPA_Basic] 프로젝션 - 여러 값 조회  (1) 2023.09.22
[JPA_Basic] 값 타입 컬렉션  (0) 2023.09.19
[JPA_Basic] Cascade  (0) 2023.09.11
[JPA_Basic] N+1 문제  (0) 2023.09.10
[JPA_Basic] Proxy  (0) 2023.09.08