프로그래밍/Spring
[ JPA ] 값 타입 리스트 동작 방식
Yanoo
2022. 1. 28. 01:06
728x90
반응형
import javax.persistence.Embeddable;
import java.util.Objects;
@Embeddable
public class Address {
private String city;
private String street;
private String zipcode;
public Address() {
}
public Address(String city, String street, String zipcode) {
this.city = city;
this.street = street;
this.zipcode = zipcode;
}
public String getCity() {
return city;
}
public String getStreet() {
return street;
}
public String getZipcode() {
return zipcode;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Address address = (Address) o;
return Objects.equals(city, address.city) && Objects.equals(street, address.street) && Objects.equals(zipcode, address.zipcode);
}
@Override
public int hashCode() {
return Objects.hash(city, street, zipcode);
}
}
import javax.persistence.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
//주소
@Embedded
private Address homeAddress;
@ElementCollection
@CollectionTable(name = "FAVORITE_FOOD", joinColumns = @JoinColumn(name = "MEMBER_ID"))
@Column(name = "FOOD_NAME")
private Set<String> favoriteFoods = new HashSet<>();
@ElementCollection
@CollectionTable(name = "ADDRESS", joinColumns = @JoinColumn(name = "MEMBER_ID"))
private List<Address> addressHistory = new ArrayList<>();
public Long getId() {
return id;
}
// ... 이하 생략
}
이런 값 타입 리스트 구조를 가진다고 했을 때 어떻게 동작할까?
Member member = new Member();
member.setUsername("member1");
member.setHomeAddress(new Address("HomeCity", "street", "10000"));
member.getFavoriteFoods().add("치킨");
member.getFavoriteFoods().add("족발");
member.getFavoriteFoods().add("피자");
member.getAddressHistory().add(new Address("old1", "street", "10000"));
member.getAddressHistory().add(new Address("old2", "street", "10000"));
em.persist(member);
em.flush();
em.clear();
System.out.println("============== START ==============");
Member findMember = em.find(Member.class, member.getId());
이렇게 만들어서 find해보면
FavoriteFoods와 addressHistory는 쿼리문을 날리지 않는다. 즉, 프록시로 가져오게 된다.
그리고 수정 시에 즉
homeCity에서 newCity로 바꾸려고 할 때,
findMember.getHomeAddress().setCity("newCity");
이렇게 진행해야할 것 같지만 이렇게 진행하면 사이드 이펙드가 생긴다.
그래서
Address a = findMember.getHomeAddress();
findMember.setHomeAddress(new Address("newCity", a.getStreet(), a.getZipcode()));
이렇게 진행하여야 한다.
치킨에서 한식으로 바꿀 때는
// 치킨 -> 한식
findMember.getFavoriteFoods().remove("치킨");
findMember.getFavoriteFoods().add("한식");
지웠다가 다시 add 한다.
old1을 newCity1으로 바꿀 때도 까다로운데, hashCode와 equals가 오버라이드 되어있다고 가정하에
findMember.getAddressHistory().remove(new Address("old1", "street", "10000"));
findMember.getAddressHistory().add(new Address("newCity1", "street", "10000"));
이렇게 진행하면 된다. 그러나 결과를 보면
멤버에 해당하는 address를 다 지우고 지운 만큼의 address를 다시 insert하는 것을 확인할 수 있다.
이 이유는
- 값 타입 컬렉션에 변경 사항이 발생하면, 주인 엔티티와 연관된 모든 데이터를 삭제하고, 값 타입 컬렉션에 있는 현재 값을 모두 다시 저장한다.
라는 값 타입의 특징 때문이다.
그래서 현업에서는 명확하지 않으면 이 값 타입을 사용하는 것을 추천하지 않는다고 한다.
728x90
반응형