값 타입
값 타입
값 타입의 종류
- 기본 값 타입
- 임베디드 타입(복합 값 타입)
- 값 타입과 불변 객체
- 값 타입의 비교
- 값 타입 컬렉션
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");
- 공유 참조로 인해 발생하는 부작용(사이드 이펙트)를 피하기 위해 값만 복사해서 사용하고, 각각 다른 객체를 참조하도록 해야 한다.
- 공유 참조는 막을 수 없으니, 객체 타입을 아예 수정하지 못하도록 만들어 상황 자체를 원천 차단하도록 해서 공유 참조가 발생하는 상황을 막는다.
- 값 타입을 불변 객체(생성 이후 값을 변경할 수 없는 객체)로 만든다.
- 생성자로만 값을 생성하고, Setter를 만들지 않는다.(수정할 일이 없고 항상 새로 만들어서 참조하게끔)
값 타입의 비교
- 동일성 비교 : == 연산자, 객체 주소가 같은 지를 보는 것
- 동등성 비교 : equlas(), 값이 같은지 비교하는 것 → equals() 메소드를 적절하게 재정의 해야 한다.
값 타입 컬렉션
- 값 타입 컬렉션은 일대다 개념으로 별도의 테이블로 저장 되어야 한다.
- 값 타입을 하나 이상 저장할 때 사용
- @ElementCollection, @CollectionTable 사용
- 컬렉션을 저장하기 위한 별도의 테이블이 필요함
- 추적할 필요도 없고 값이 바뀌어도 업데이트 할 필요 없을 때
- 기본 값이 Lazy loading
- 실무에서는 값 타입 대신에 일대다 관계를 사용할 것을 고려, 웬만하면 값 타입 컬렉션 사용 X
예제
@Entity
public class Member {
...
@ElementCollection
@CollectionTable(name = "FAVORITE_FOOD", joinColumns = @JoinColumn(name = "MEMBER_ID"))
private Set<String> favoriteFoods = new HashSet<>();
}
728x90
'# 강의 > [인프런] 자바 ORM 표준 JPA 프로그래밍' 카테고리의 다른 글
프록시와 연관관계 관리 (0) | 2022.05.09 |
---|---|
고급 매핑(상속, Mapped Superclass) (0) | 2022.02.20 |
엔티티 매핑 2 (연관관계) (0) | 2022.01.12 |
엔티티 매핑 1(테이블, 컬럼, 키 매핑 전략) (0) | 2022.01.12 |
영속성 관리 - 내부 동작 방식 (0) | 2022.01.12 |
JPA는 왜 사용해야 하는가? (0) | 2022.01.06 |