JPA의 데이터 타입은 2가지가 있습니다.
엔티티(Entity) 타입
@Entity
로 정의하는 객체값 타입
int age; // 자바 기본 타입(primitive type)
Integer count; // Wrapper 클래스
String name;
기본 값 타입에는 자바 기본 타입, 래퍼(Wrapper) 클래스, String 등이 포함되고, 기본 값 타입의 생명주기는 Entity에 의존합니다. 예를 들어 회원을 삭제하면 회원의 이름, 나이 Field도 함께 삭제됩니다.
자바의 기본 타입은 절대 공유해서는 안되며 항상 값을 복사하도록 동작합니다. Integer 같은 래퍼 클래스나 String 같은 특수한 클래스는 공유 가능한 객체이지만 불변 객체로서 한 번 만들어진 객체는 데이터 수정이 불가합니다.
주로 기본 값 타입을 모아서 새로운 값 타입을 직접 정의 하는 것으로 재사용성이 높습니다. 아래의 회원
엔티티에서 기본 값 타입을 모아서 임베디드 타입으로 리팩토링 하면 다음과 같습니다.
회원 엔티티는 이름, 근무 시작일, 근무 종료일, 주소 도시, 주소 번지, 주소 우편번호를 가진다.
회원 엔티티는 이름, 근무 기간, 집 주소를 가진다.
회원
엔티티@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
@Embedded
private Period workPeriod;
@Embedded
private Address homeAddress;
}
Period
임베디드 타입@Embeddable
@NoArgsConstructor
public class Period {
private ZonedDateTime startDate;
private ZonedDateTime endDate;
}
Address
임베디드 타입@Embeddable
@NoArgsConstructor
public class Address {
private String city;
private String street;
@Column(name = "zipcode")
private String zipCode;
}
Embedded 값 타입을 정의하는 곳에 @Embeddable
, 값 타입을 실제 사용하는 곳에는 @Embedded
표시합니다. 추가적으로 임베디드 타입의 경우 기본 생성자가 필수로 존재해야 합니다.
임베디드 타입은 엔티티의 값일 뿐이며 임베디드 타입을 사용하기 전과 후의 변함은 없어야 합니다. 또한 임베디드 타입의 값이 null이면 매핑한 컬럼 값은 모두 null이 됩니다.
한 엔티티에서 같은 값 타입을 사용하면 @AttributeOverride
를 통해 컬러 명 속성을 재정의 합니다.
@Embedded
private Address homeAddress;
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "city", column = @Column(name = "work_city")),
@AttributeOverride(name = "street", column = @Column(name = "work_street")),
@AttributeOverride(name = "zipCode", column = @Column(name = "work_zip_code"))
})
private Address companyAddress;
예제 결과
값 타입을 여러 Entity에서 공유 참조 하면 부작용(side effect) 발생할 수 있습니다. 자바 기본 타입은 값을 대입하면 항상 값을 복사해서 사용하지만 Embedded 타입의 경우는 직접 정의한 객체 타입으로 값을 대입하면 참조 값을 공유합니다. 객체의 공유 참조는 막을 수 없지만 공유되더라도 값을 변경하지 못하도록 불변 객체 설정을 함으로써 부작용을 막아야합니다.
// 기본 타입(primitive type)
int a = 10;
int b = a;
// 객체 타입
Address a = new Address(“Old”);
Address b = a; //객체 타입은 참조를 전달
b.setCity(“New”)
불변 객체
값 타입은 생성 시점 이후 절대 값을 변경할 수 없는 객체, 즉 불변 객체로 설계합니다. 생성자로만 값을 설정하고 수정자(Setter)를 만들지 않으면 됩니다.
ex) Integer
, String
은 자바가 제공하는 대표적인 불변 객체
값 타입은 a.equals(b)
를 사용해서 동등성 비교를 해야 함
값 타입을 하나 이상 저장할 때, List
나 Set
과 같은 Collection을 사용합니다.DB는 컬렉션을 같은 테이블에 저장할 수 없기 때문에, 컬렉션을 저장하기 위한 별도의 테이블이 필요합니다.
@ElementCollection(fetch = FetchType.LAZY)
@CollectionTable(name = "FAVORITE_FOOD", joinColumns = {
@JoinColumn(name = "MEMBER_ID")
})
@Column(name = "FOOD_NAME")
private Set<String> favoriteFoods = new HashSet<>();
@ElementCollection(fetch = FetchType.LAZY)
@CollectionTable(name = "ADDRESS", joinColumns = {
@JoinColumn(name = "MEMBER_ID")
})
private List<Address> addressHistory = new ArrayList<>();
@ElementCollection
, @CollectionTable
을 통해 테이블 설정을 할 수 있지만 Collection 값 타입은 다음과 같은 제약사항으로 인해 사용하지 않은 것을 권장합니다.
Collection 값 타입 대신에 1:N
일대다 관계로 Entity를 만들고,해당 Entity에서 영속성 전이, 고아 객체 옵션과 함께 값 타입 Collection처럼 사용합니다.
👉 실전 예제 코드