[자바 ORM 표준 JPA 프로그래밍] 값 타입

hyeseungS·2022년 5월 13일
0

9.3 값 타입과 불변 객체

값 타입은 복잡한 객체 세상을 단순화하려고 만든 개념이다.
따라서 값 타입은 단순하고 안전하게 다룰 수 있어야 한다.

9.3.1 값 타입 공유 참조

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

member1.setHomeAddress(new Address("OldCity"));
Address address = member1.getHomeAddress();

address.setCity("NewCity"); // 회원1의 address 값을 공유해서 사용
member2.setHomeAddress(address);
"회원2의 주소만 "NewCity"로 변경되길 기대했지만 회원1의 주소도 "NewCity"로 변경"
-> 회원1과 회원2가 같은 address 인스턴스를 참조 
-> 영속성 컨텍스트는 회원1과 회원2 둘 다 city 속성이 변경된 것으로 판단 
-> 회원1, 회원2 각각 UPDATE SQL 실행

이러한 "부작용"을 막기 위해서는 값을 복사해서 사용!

9.3.2 값 타입 복사

값(인스턴스)을 복사해서 사용해야 한다.

member1.setHomeAddress(new Address("OldCity"));
Address address = member1.getHomeAddress();

// 회원1의 address 값을 복사해 새로운 newAddress 값을 생성
Address newAddress = address.clone();

newAddress.setCity("NewCity");
member2.setHomeAddress(newAddress);
"회원2의 주소만 "NewCity"로 변경"
-> clone() 메소드는 자신을 복사해서 반환하도록 구현해 회원2에 새로운 주소 할당
-> 영속성 컨텍스트는 회원2의 주소만 변경된 것으로 판단
-> 회원2만 UPDATE SQL 실행

공유 참조로 인해 발생하는 부작용 피할 수 있음!
but, 임베디드 타입처럼 직접 정의한 값 타입은 자바의 기본 타입이 아닌 객체 타입!

  • 자바는 기본타입에 값을 대입하면 값을 복사해서 전달.
int a = 10;
int b = a; // 기본 타입은 항상 값을 복사
b = 4;
"a = 10, b = 4"
-> int b = a에서 a의 값 10을 복사해 b에 넘겨서 a, b는 완전히 독립된 값 & 부작용 X
  • 자바는 객체에 값을 대입하면 항상 참조값 전달
Address a = new Address("Old");
Address b = a; // 객체 타입은 항상 참조 값을 전달
b.setCity("New");
"a.city = "New", b.city = "New"
-> Address b = a에서 a가 참조하는 인스턴스의 참조 값을 b에 넘겨서 a와 b는 같은 인스턴스 공유참조 & 부작용 O

객체를 대입할 때마다 인스턴스를 복사해 대입하면 공유 참조 피할 수 있지만,
복사하지 않고 원본의 참조 값을 직접 넘기는 것을 막을 방법은 없음"

Address a = new Address("Old");
Address b = a.clone(); // 항상 복사해서 넘겨야 한다.
// Address b = a; // 부작용 발생!!
b.setCity("New");

객체의 공유 참조는 피할 수 없으므로, 근본적인 해결책 필요
-> 객체의 값을 수정하지 못하게 막자!

9.3.3 불변 객체

객체를 불변하게 만들면 값을 수정할 수 없으므로 부작용을 원천 차단 가능.
따라서 값 타입은 되도록 불변 객체(immutable Oject)로 설계하자!

  • 불변 객체 : 한 번 만들면 절대 변경할 수 없는 객체
    • 값 조회 O, 값 수정 X
// 주소 불변 객체
@Embeddable
public class Address {
	
	private String city;
        
	protected Address() {} // JPA 에서 기본 생성자는 필수!
        
	// 생성자로 초기 값 설정
	public Address(String city) {this.city = city;}

	// 접근자(Getter)는 노출
	public String getCity() {return city;}
        
	// 수정자(Setter)는 만들지 않음
}

불변이라는 작은 제약으로 부작용이라는 큰 재앙을 막을 수 있다!

9.4 값 타입의 비교

int a = 10;
int b = 10;

Address a = new Address("서울시", "종로구", "1번지);
Address b = new Address("서울시", "종로구", "1번지);
- int a의 숫자 10과 int b의 숫자 10 같다고 표현한다.
- Address a와 Address b는 같다고 표현한다.
  • 자바가 제공하는 객체 비교
    • 동일성(Identity) 비교 : 인스턴스의 참조 값 비교, == 사용
    • 동등성(Equivalence) 비교 : 인스턴스의 값 비교, equals() 사용
ex) Address 값 타입
- a == b : 둘은 서로 다른 인스턴스로 결과는 "거짓"
- but, 값 타입은 인스턴스가 달라도 그 안에 값이 같으면 같은 것으로 봐야하므로
  a.equals(b)의 동등성 비교를 통해 값 타입을 비교해야함.
- Address의 equals() 메소드를 재정의 해야함 -> 모든 필드의 값을 비교하도록 구현

+) 자바는 equals() 재정의하면 hashCode()도 재정의하는 것이 안전 
   그렇지 않으면 해시를 사용하는 컬렉션 (HashSet, HashMap) 정상 동작 X
   자바 IDE에는 대부분 equals, hashCode 메소드 자동으로 생성해주는 기능 있음

출처 : 자바 ORM 표준 JPA 프로그래밍 책


[자바 ORM 표준 JPA 프로그래밍] 객체지향 쿼리 언어


profile
Studying!!

0개의 댓글