Programming/JPA(KYH)

JPA - 값 타입과 불변 객체, 값 타입의 비교

잇(IT) 2023. 7. 17. 16:19

- 값 타입과 불변 객체

 

- 값 타입 공유 참조

1. 임베디드 타입 같은 값 타입을 여러 엔티티에서 공유하면 위험하다.

2. 부작용(side effect) 발생한다.

 

Address address = new Address("city", "street", "zipcode");

            Member member1 = new Member();
            member1.setUsername("member1");
            member1.setHomeAddress(address);
            em.persist(member1);

            Member member2 = new Member();
            member2.setUsername("member2");
            member2.setHomeAddress(address);
            em.persist(member2);

            member1.getHomeAddress().setCity("new city");

- 같은 엔티티를 사용하기 때문에 값을 변경하게 되면 해당 엔티티를 참조하는 모든 값이 전부 바뀌게 된다.

 

- 대신 값(인스턴스)를 복사해서 사용하면 위와 같은 상황을 해결 할 수 있다.

Address address = new Address("city", "street", "zipcode");

            Member member1 = new Member();
            member1.setUsername("member1");
            member1.setHomeAddress(address);
            em.persist(member1);

            Address newAddress = new Address("city", "street", "zipcode");

            Member member2 = new Member();
            member2.setUsername("member2");
            member2.setHomeAddress(newAddress);
            em.persist(member2);

            member1.getHomeAddress().setCity("new city");

- 위와 같이 엔티티를 복사하여 사용했기 때문에 다른 엔티티에 영향을 주지 않는다.

 

- 객체 타입의 한계

1. 항상 값을 복사해서 사용하면 공유 참조로 인해 발생하는 부작용 을 피할 수 있다.

2. 문제는 임베디드 타입처럼 직접 정의한 값 타입은 자바의 기본 타입이 아니라 객체 타입이다. 

3. 자바 기본 타입에 값을 대입하면 값을 복사한다.

4. 객체 타입은 참조 값을 직접 대입하는 것을 막을 방법이 없다.

5. 객체의 공유 참조는 피할 수 없다.


- 기본 타입

int a = 10;
itn b = a;
a = 4;
//a 변수에 새로운 값을 넣어도 값을 참조하기 때문에 b에는 영향을 주지 않는다.

Address a = new Address("OLD");
Address b = a; //객체 타입은 참조를 전달
b.setCity("NEW")
//참조값을 변경하게 되면 a의 참조값에도 영향을 주게 된다.

- 불변 객체

1. 객체 타입을 수정할 수 없게 만들면 부작용을 원천 차단할 수 있다.

2. 값 타입은 불변 객체로 설계해야 한다.

3. 불변 객체 : 생성 시점 이후 절대 값을 변경할 수 없는 객체이다.

4. 생성자로만 값을 설정하고 수정자(Setter)를 만들지 않으면 된다.

5. Integer, String은 자바가 제공하는 대표적인 불변 객체다.

 

Address address = new Address("city", "street", "zipcode");

Member member1 = new Member();
member1.setUsername("member1");
member1.setHomeAddress(address);
em.persist(member1);

Address newAddress = new Address("NewCity", address.getStreet(), address.getZipcode());
member1.setHomeAddress(newAddress);

- 위와 같이 생성자를 통해 값을 주입해줘야 한다. 서로 다른 값이 들어간다면 새롭게 생성자를 통해 생성해준 값을 넣어주면 된다.

- 기존의 값을 변경하고 싶다면 임베디드 타입의 객체에 새로운 객체를 생성하여 넣어줘야 한다.

 

- 함께 공유해서 사용하고 싶다면 값 타입이 아닌 엔티티를 사용해야 한다.


- 값 타입의 비교

 

public class ValueMain {
    public static void main(String[] args) {
        int a = 10;
        int b = 10;

        System.out.println("a == b = " + (a == b));

        Address address1 = new Address("city", "street", "10000");
        Address address2 = new Address("city", "street", "10000");

        System.out.println("(address1 == address2) = " + (address1 == address2));
        System.out.println("address1.equals(address2) = " + address1.equals(address2));
    }
}

- address1과 address2는 서로 다른 참조값을 가지기 때문에 비교하였을 때 false가 나온다.

- equals의 기본 비교 연산자는 ==이기 때문에 값을 비교하기 위해선 JAVA가 제공하는 equals 메서드를 오버라이드 하여 사용해야 한다.

@Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Address address = (Address) o;
        return Objects.equals(city, address.city) &&
                Objects.equals(street, address.street) &&
                Objects.equals(zipcode, address.zipcode);
    }

    @Override
    public int hashCode() {
        return Objects.hash(city, street, zipcode);
    }

- 새로운 equals() 메서드를 적용한 뒤 결과값이 변경된 것을 확인 할 수 있다.

 

 

 

 

 

 

 

 

 

 

 

출처 : 인프런 - 우아한 형제들 기술이사 김영한의 스프링 완전 정복 (자바 ORM 표준 JPA 프로그래밍 - 기본편)

728x90