728x90
값 타입 컬렉션이란?
- 값 타입을 하나 이상 저장할 때 사용한다.
- 컬렉션을 저장하기 위한 별도의 테이블이 필요하다.
- 데이터베이스는 컬렉션을 같은 테이블에 저장할 수 없다.
- @ElementCollection, @CollectionTable 을 사용한다.
값 타입 컬렉션 정의 예제 코드)
public class Member {
@Id
@GeneratedValue
private Long id;
@Column(name="USERNAME")
private String name;
@Embedded
private Address homeAddress;
@ElementCollection
@CollectionTable(name = "FAVORITE_FOOD", joinColumns = @JoinColumn(name = "MEMBER_ID"))
private Set<String> favoriteFoods = new HashSet<>();
@ElementCollection
@CollectionTable(name = "ADDRESS", joinColumns = @JoinColumn(name = "MEMBER_ID"))
private List<Address> addresseHistory = new ArrayList<>();
@Embeddable
public class Address {
private String city;
private String street;
private String zipcode;
public Address(String city, String street, String zipcode) {
this.city = city;
this.street = street;
this.zipcode = zipcode;
}
값 타임 저장 예제
값 타입 복사를 사용한다.
Member member = new Member();
member.setName("mem1");
member.setHomeAddress(new Address("homeCity","street", "10000"));
member.getFavoriteFoods().add("치킨");
member.getFavoriteFoods().add("마라샹궈");
member.getFavoriteFoods().add("떡볶이");
member.getAddresseHistory().add(new Address("old1","street", "10000"));
member.getAddresseHistory().add(new Address("old2","street", "10000"));
em.persist(member);
값 타임 조회 예제
값 타입 컬렉션도 지연 로딩 전략을 사용한다.
참고: 값 타입 컬렉션은 영속성 전이에(Cascade) 고아 객체 제거 기능을 필수로 가진다고 볼 수 있다.
Member findMember = em.find(Member.class, member.getId());
List<Address> addresseHistory = findMember.getAddresseHistory();
for(Address address: addresseHistory){
System.out.println("address.getCity() = " + address.getCity());
}
Set<String> favoriteFoods = findMember.getFavoriteFoods();
for(String favoriteFood:favoriteFoods){
System.out.println("favoriteFood = " + favoriteFood);
}
값 타임 수정 예제
//homeCity -> newCity
Address homeAddress = findMember.getHomeAddress();
findMember.setHomeAddress(new Address("newCity",homeAddress.getStreet(),homeAddress.getZipcode()));
//치킨 삭제 -> 김치찌개 추가
findMember.getFavoriteFoods().remove("치킨");
findMember.getFavoriteFoods().add("김치찌개");
//old1 삭제 -> new1 추가
findMember.getAddresseHistory().remove(new Address("old1","street", "10000"));
findMember.getAddresseHistory().add(new Address("new1","street", "10000"));
여기서 old1을 삭제하고 new1만 추가하는 간단한 동작이 일어나면 좋으련만...
실제로는 old1, old2를 다 삭제한 후에 다시 old2, new1을 저장하는 방식으로 동작한다.
왜 그럴까?
.
.
.
값 타입 컬렉션의 제약 사항
- 값 타입은 엔티티와 다르게 식별자 개념이 없다.
- 즉, 값은 변경하면 추적하기 어렵다.
- 그래서 값 타입 컬렉션에 변경 사항이 발생하면, 주인 엔티티와 연관된 모든 데이터를 삭제하고, 값 타입 컬렉션에 있는 현재 값을 모두 다시 저장한다.
- 값 타입 컬렉션을 매핑하는 테이블은 모든 컬럼을 묶어서 기본 키를 구성해야 한다.
- null 입력 X, 중복 저장 X
값 타입 컬렉션 대안
- 실무에서는 상황에 따라 값 타입 컬렉션 대신에 일대다 관계를 고려한다.
- 일대다 관계를 위한 엔티티를 만들고, 여기에 값 타입을 사용한다.
- 영속성 전이 (Cascade) + 고아 객체 제거를 사용해서 값 타입 컬렉션처럼 사용
- Ex)AddressEntity
//Member.java
@Entity
public class Member {
...
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "MEMBER_ID")
private List<AddressEntity> addresseHistory = new ArrayList<>();
...
}
//AddressEntity.java
@Entity
@Table(name = "ADDRESS")
public class AddressEntity {
@Id @GeneratedValue
private Long id;
private Address address;
public AddressEntity(String city, String street, String zipcode) {
this.address = new Address(city, street, zipcode);
}
}
//main
findMember.getAddresseHistory().add(new AddressEntity("new1","street", "10000"));
정리하자면...
엔티티 타입의 특징
- 식별자 있음
- 생명 주기 관리
- 공유
값 타입의 특징
- 식별자 없음
- 생명 주기를 엔티티에 의존
- 공유하지 않는 것이 안전(복사해서 사용)
- 불변 객체로 만드는 것이 안전
728x90
'💻dev > 🌱Java+Spring' 카테고리의 다른 글
JPA | 예제로 알아보는 값 매핑 (예제 - 6) (0) | 2023.05.02 |
---|---|
JPA | 예제로 알아보는 영속성 전이 (예제 - 5) (0) | 2023.05.02 |
JPA | 값 타입은 변경이 가능할까? 불변 객체(immutable object)란? (+ 값 타입을 비교하는 방법) (0) | 2023.05.02 |
JPA | 기본값 타입과 임베디드 타입이란? (0) | 2023.05.01 |
JPA | 영속성 전이(CASCADE)와 고아 객체란? (0) | 2023.05.01 |