[完] 값 타입

값 타입

 

 

값 타입

값 타입의 종류

  1. 기본 값 타입
  2. 임베디드 타입(복합 값 타입)
  3. 값 타입과 불변 객체
  4. 값 타입의 비교
  5. 값 타입 컬렉션

 

 

JPA의 데이터 타입 분류

엔티티 타입

  • @Entity로 정의하는 객체
  • 데이터가 변해도 식별자로 지속해서 추적 가능

값 타입

  • int, Integer, String 등 단순히 값으로 사용하는 자바 기본 타입
  • 식별자가 없고 값만 있으므로 변경 시 추적 불가

 

 

값 타입 분류

1. 기본 값 타입

  • String name, int age ... 와 같은 것들
  • 생명주기를 엔티티에 의존
  • 값 타입은 공유하면 안된다.
    - 어떤 객체의 값을 바꿨는데 다른 객체의 값이 바뀌면 안된다.
  • 값은 항상 복사된다(Call by value)
    - 예외로 Wrapper 클래스는 특수한 클래스로 공유 가능

2. 임베디드 타입

  • 새로운 값 타입을 직접 정의
  • 복합 값 타입
  • 용도
    • x, y 좌표를 묶은 position 값
    • started_at, ended_at을 묶은 period 값
  • 엔티티의 Field를 임베디드 타입으로 구성할 수 있다.
  • 재사용이 좋다
  • 높은 응집도
  • 해당 값 타입만 사용하는 의미 있는 메소드를 만들 수 있다
  • 객체와 테이블을 세밀하게 매핑할 수 있다.

 

임베디드 타입의 사용

@Embeddable // 값 타입을 정의하는 곳에 표시
@Embedded // 값 타입을  사용하는 곳에 표시

 

 

임베디드 타입의 사용 예시

@Getter
@NoArgsConstructor
@Embeddable
public class Period {
    private LocalDateTime startDate;
    private LocalDateTime endDate;

    public boolean isWork(LocalDatetime date) {
        if(date가 startDate와 endDate 사이값이면){
            return true;
        }
        return false;
    }

    public Period(LocalDateTime startDate, LocalDateTime endDate) {
        this.startDate = startDate;
        this.endDate = endDate;
    }

}

 

@Entity
public class Program {

    ...
		
    @AttributeOverrides({
        @AttributeOverride(name="startDate", column=@Column("apply_start_date")),
        @AttributeOverride(name="startDate", column=@Column("apply_end_date"))
    })
    @Embedded
    private Period applyPeriod;
		
    @AttributeOverrides({
        @AttributeOverride(name="startDate", column=@Column("landing_start_date")),
        @AttributeOverride(name="startDate", column=@Column("landing_end_date"))
    })
    @Embedded
    private Period landingPagePeriod;

}

 

@Embedded로 선언한 컬럼이 하나라면, 임베디드 값 필드 2개(start_date, end_date)가 테이블에 매핑되지만, @Embedded로 선언한 컬럼이 두개라면 @AttributeOverrides로 각각 컬럼명을 중복되지 않게 정의해야 한다.

 

 

 

임베디드 타입 사용 시 주의할 점

// Before
Address address = new Address("seoul", "jongro");
Member member = memberRepository.findById(1L);
Member member2 = memberRepository.findById(2L);

member.setAddress(address);
member2.setAddress(address);

// 이 경우 member2까지 값 변경이 일어나는 사이드 이펙트 발생
member.getAddress.setCity("Busan");


// After
Address address = new Address("seoul", "jongro");
Address newAddress = new Address(address.getCity(), address.getRoad());

Member member = memberRepository.findById(1L);
Member member2 = memberRepository.findById(2L);

member.setAddress(address);
member2.setAddress(newAddress);

// member와 member2가 다른 임베디드 타입 객체를 참조하도록 해야 함
member.getAddress.setCity("Busan");

 

  1. 공유 참조로 인해 발생하는 부작용(사이드 이펙트) 피하기 위해 값만 복사해서 사용하고, 각각 다른 객체를 참조하도록 해야 한다.
  2. 공유 참조는 막을 없으니, 객체 타입을 아예 수정하지 못하도록 만들어 상황 자체를 원천 차단하도록 해서 공유 참조가 발생하는 상황을 막는다.
  3. 타입을 불변 객체(생성 이후 값을 변경할 없는 객체) 만든다.
  4. 생성자로만 값을 생성하고, Setter 만들지 않는다.(수정할 일이 없고 항상 새로 만들어서 참조하게끔)

 

값 타입의 비교

  1. 동일성 비교 : == 연산자, 객체 주소가 같은 지를 보는 것
  2. 동등성 비교 : equlas(), 값이 같은지 비교하는 것 → equals() 메소드를 적절하게 재정의 해야 한다.

 

 

값 타입 컬렉션

  1. 값 타입 컬렉션은 일대다 개념으로 별도의 테이블로 저장 되어야 한다.
  2. 값 타입을 하나 이상 저장할 때 사용
  3. @ElementCollection, @CollectionTable 사용
  4. 컬렉션을 저장하기 위한 별도의 테이블이 필요함
  5. 추적할 필요도 없고 값이 바뀌어도 업데이트 할 필요 없을 때
  6. 기본 값이 Lazy loading
  7. 실무에서는 값 타입 대신에 일대다 관계를 사용할 것을 고려, 웬만하면 값 타입 컬렉션 사용 X

 

예제

@Entity
public class Member {

    ...
		
    @ElementCollection
    @CollectionTable(name = "FAVORITE_FOOD", joinColumns = @JoinColumn(name = "MEMBER_ID"))
    private Set<String> favoriteFoods = new HashSet<>();

}

 

 

 

 

 

 

 

728x90