흰 스타렉스에서 내가 내리지

[JPA] 값 타입1 - 복합, 연관관계, 재정의, 복사, 비교 본문

Spring

[JPA] 값 타입1 - 복합, 연관관계, 재정의, 복사, 비교

주씨. 2023. 2. 19. 17:50
728x90

임베디드 타입 (복합 값 타입)

public class Member{
    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    @Embedded Period workPeriod;
    @Embedded Address homeAddress;
@Embeddable
public class Period {
    @Temporal(TemporalType.DATE)
    Date startDate;
    
    @Temporal(TemporalType.DATE)
    Date endDate;
}
@Embeddable
public class Address {
    @Column(name = "city")
    private String city;
    private String street;
    private String zipcode;
}

- startDate, endDate를 합해서 Period 클래스를 만들었다.

- city, street, zipcode 를 합해서 Address 클래스를 만들었다.

 

임베디드 타입을 사용하려면 다음 2가지 어노테이션이 필요하다. 둘 중 하나는 생략해도 된다.

- @Embeddable : 값 타입을 정의하는 곳에 표시

- @Embedded : 값 타입을 사용하는 곳에 표시

 

 

 

임베디드 타입과 연관관계

@Entity
@Getter @Setter
public class Member{
    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    @Embedded Address address;
    @Embedded PhoneNumber phoneNumber;
}

@Embeddable
public class Address {
    @Column(name = "city")
    private String city;

    private String street;
    private String state;
    @Embedded Zipcode zipcode;  // 임베디드 타입 포함
}


@Embeddable
public class Zipcode {
    String zip;
    String plusFour;
}

@Embeddable
public class PhoneNumber {
    String areaCode;
    String localNumber;
    @ManyToOne PhoneServiceProvider provider;
}

@Entity
public class PhoneServiceProvider {
    @Id String name;
}

 

 

@AttributeOverride : 속성 재정의

@Entity
@Getter @Setter
public class Member{
    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    @Embedded Address homeAddress;
    @Embedded Address companyAddress;

집 주소에 회사 주소를 하나 더 추가했다.

문제는 테이블에 매핑하는 컬럼명이 중복되는 것이다. 

이때는 @AttributeOverrides 를 사용해서 매핑정보를 재정의해야 한다. 

 

@Embedded private Address homeAddress;

@Embedded
@AttributeOverrides({
        @AttributeOverride(name = "city", column = @Column(name = "COMPANY_CITY")),
        @AttributeOverride(name = "street", column = @Column(name = "COMPANY_STREET")),
        @AttributeOverride(name = "zipcode", column = @Column(name = "COMPANY_ZIPCODE"))
})
private Address companyAddress;

 

임베디드 타입이 null 이면 매핑한 컬럼 값은 모두 null이 된다. 

 

값 타입 복사

값 타입의 실제 인스턴스인 값을 공유하는 것은 위험하다. 

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

Address a = new Address("Old");
Address b = a.clone();	// 항상 복사해서 넘겨야 한다. 
// Address b = a;	// 이렇게 참조만 넘기면 부작용이 발생할 수 있다. 
b.setCity("New");

복사를 하지 않고 넘기면, a와 b가 참조하는 인스턴스가 같기 때문에 마지막 줄 코드를 실행하면 a의 City 값도 바뀐다. 

 

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

근본적인 해결책은 해당 필드의 Setter를 만들지 않는 것이다. 

이렇게 하면 공유 참조를 해도 값을 변경하지 못하므로 부작용의 발생을 막을 수 있다. 

값 타입은 될 수 있으면 불변 객체(immutable Obejct)로 설계해야 한다. 

 

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

 

 

값 타입의 비교

자바가 제공하는 객체 비교는 2가지다. 

- 동일성 (Identity) 비교 : 인스턴스의 참조 값을 비교, == 사용

- 동등성 (Equivalence) 비교 : 인스턴스의 값을 비교, equals() 사용

 

값 타입은 비록 인스턴스가 달라도 그 안에 값이 같으면 같은 것으로 봐야 한다. 

따라서 값 타입을 비교할 때는 a.equals(b)를 사용해서 동등성 비교를 해야 한다. 

물론 Address의 equals() 메소드를 재정의해야 한다.