•
JPA의 데이터 타입을 가장 크게 분류하면 엔티티 타입과 값 타입으로 나눌 수 있다.
◦
엔티티 타입은 @Entity로 정의하는 객체이고 값 타입은 int, Integer, String처럼 단순히 값으로 사용하는 원시형, 참조형 타입을 의미한다.
•
값 타입은 식별자가 없고 숫자나 문자같은 속성만 있으므로 추적할 수 없으며 다음과 같이 셋으로 분류할 수 있다.
◦
기본값 타입
▪
자바 기본 타입
▪
래퍼 클래스
▪
String
◦
임베디드 타입
◦
컬렉션 값 타입
1. 기본값 타입
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
private String name; // 기본값 타입
private int age; // 기본값 타입
}
Java
복사
•
Member 엔티티는 id라는 식별자 값도 가지고 생명주기도 있지만 값 타입인 name, age 속성은 식별자 값도 없고 생명주기도 회원 엔티티에 의존한다.
2. 임베디드 타입(복합 값 타입)
•
새로운 값 타입을 직접 정의해서 사용할 수 있으며 이를 임베디드 타입이라고 한다.
@Entity
public class Member {
@Id @GeneratedValue
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;
}
Java
복사
•
@Embeddable, @Embedded을 이용해 임베디드 타입을 설정하였다. 참고로 임베디드 타입은 기본 생성자가 필수다.
•
임베디드 타입을 포함한 모든 값 타입은 엔티티의 생명주기에 의존하므로 엔티티와 임베디드 타입의 관계를 UML로 표현하면 구성 관계가 된다.
1. 임베디드 타입과 테이블 매핑
•
그렇다면 임베디드 타입을 테이블에 어떻게 매핑할까?
•
임베디드 타입은 엔티티의 값일 뿐으로 값이 속한 엔티티의 테이블에 매핑한다.
2. 임베디드 타입과 연관관계
•
임베디드 타입은 값 타입을 포함하는 기능 외에도 엔티티를 참조할 수 있다.
3. @AttributeOverride: 속성 재정의
•
임베디드 타입에 정의한 매핑정보를 재정의하려면 엔티티에 @AttributeOverride를 사용하면 된다.
•
동일한 임베디드 타입을 재사용하게되는 경우, 테이블에 매핑하는 컬럼명이 중복될 것이다. 이때 @AttributeOverride를 사용해서 컬럼명들을 매핑해주어야 한다.
4. 임베디드 타입과 null
•
임베디드 타입이 null이면 매핑한 컬럼 값은 모두 null이 된다.
3. 값 타입과 불변 객체
1. 값 타입 공유 참조
•
임베디드 타입 같은 값 타입을 여러 엔티티에서 공유하면 문제가 발생할 가능성이 매우 높다.
2. 값 타입 복사
•
값 타입의 실제 인스턴스인 값을 공유하는 것이 위험하므로 대신 값을 복사해서 사용해야 한다.
3. 불변 객체
•
값 타입은 사이드 이펙트 걱정 없이 사용할 수 있어야 한다. 객체를 불변하게 만들면 값을 수정할 수 없으므로 부작용을 원천 차단할 수 있다.
•
값 타입은 될 수 있으면 불변 객체로 설계해야 한다.
4. 값 타입의 비교
•
값 타입은 비록 인스턴스가 달라도 그 안의 값이 같다면 같은 것으로 봐야하므로 동등성 비교를 해야 한다.
•
따라서 값 타입 내부에 hashCode(), equals() 메소드를 적절히 재정의하여 동등성 비교를 수행할 수 있도록 해야 한다.
5. 값 타입 컬렉션
•
값 타입을 하나 이상 저장하려면 컬렉션에 보관하고 @ElementCollection, @CollectionTable 어노테이션을 사용하면 된다.
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@Embedded
private Address homeAddress;
@ElementCollection
@CollectionTable(name = "FAVORITE_FOODS", joinColumns = @JoinColumn(name = "MEMBER_ID"))
@Column(name = "FOOD_NAME")
private Set<String> favoriteFoods = new HashSet<String>();
@ElementCollection
@CollectionTable(name = "ADDRESS", joinColumns = @JoinColumn(name = "MEMBER_ID"))
private List<Address> addressHistory = new ArrayList<Address>();
...
}
@Embeddable
public class Address {
@Column
private String city;
private String street;
private String zipCode;
...
}
Java
복사
1. 값 타입 컬렉션 사용
•
값 타입 컬렉션도 조회 시 페치 전략을 선택할 수 있으며 LAZY가 기본이다. 또한 영속성 전이 + 고아 객체 제거 기능을 필수로 가진다고 볼 수 있다.
2. 값 타입 컬렉션의 제약사항
•
엔티티는 식별자가 있으므로 엔티티의 값을 변경해도 식별자로 데이터베이스에 저장된 원본 데이터르 쉽게 찾아 변경할 수 있다.
•
그러나 값 타입은 식별자라는 개념이 없고 단순한 값들의 모음이므로 값을 변경해버리면 데이터베이스에 저장된 원본 데이터를 찾기 어렵다.
•
특정 엔티티 하나에 소속된 값 타입은 값이 변경되어도 자신이 소속된 엔티티를 데이터베이스에서 찾아 값을 변경하면 된다.
•
문제는 값 타입 컬렉션이다. 값 타입 컬렉션에 보관된 값 타입들은 별도의 테이블에 보관된다. 따라서 여기에 보관된 값 타입의 값이 변경되면 데이터베이스에 있는 원본 데이터를 찾기 어렵다.
•
이런 문제로 인해 JPA 구현체들은 값 타입 컬렉션이 매핑된 테이블의 연관된 모든 데이터를 삭제하고 현재 값 타입 컬렉션 객체의 모든 값을 데이터베이스에 다시 저장하는 식으로 해결한다.
•
엔티티를 만들어서 연관관계를 매핑해주는 게 더 나을 것 같다.
6. 정리
엔티티 타입의 특징
•
식별자가 있다.
•
생명 주기가 있다.
•
공유할 수 있다.
값 타입의 특징
•
식별자가 없다.
•
생명주기가 엔티티에 종속되어 있다.
•
공유하지 않는 것이 안전하다.
•