엔티티 타입
값 타입
값 타입 분류
기본 값 타입
int a = 10;
int b = a;
b = 20;
// a = 10, b= 20 (b는 a와 공유되지 않음)
public class Member extends BaseEntity {
...
@Embedded
private Period period
/* 기간 Period
private LocalDateTime startDate;
private LocalDateTime endDate;
*/
@Embedded
private Address address;
/* 주소 Address
priate String city;
private String street;
private String zipcode;
*/
}
@Embeddable
public class Period {
private LocalDateTime startDate;
private LocalDateTime endDate;
public Period() {}
}
@Embeddable
public class Address {
private String city;
private String street;
private String zipcode;
public Address() {}
}
임베디드 타입의 장점
임베디드 타입과 테이블 매핑
임베디드 타입과 연관관계
@AttributeOverride : 속성 재정의
@Embedded
private Address homeAddress; // ADDRESS
@Embedded
private Address workAddress; // ADDRESS
@Embedded
private Address homeAddress; // ADDRESS
@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_ZIPCODE")),
private Address workAddress; // 재정의
임베디드 타입과 null : 임베디드 타입의 값이 null이면 매핑한 컬럼 값은 모두 null
값 타입은 복잡한 객체 세상을 조금이라도 단순화하려고 만든 개념이다.
따라서 값 타입은 단순하고 안전하게 다룰 수 있어야 한다.
값 타입 공유 참조
Address address = new Adress("city", "street", "10000");
Member member = new Member();
member.setHomeAddress(address);
em.persist(member);
Member member2 = new Member();
member2.setHomeAddress(address);
em.persist(member2);
// member, member2는 동일한 주소를 가지고 있음
// member의 Address의 city를 변경하고 싶다!
member.getHomeAddress().setCity("new city);
// 위를 진행하면 member만 변경되는 것이 아니라 member2도 같이 변경됨 (address를 공유하고 있으므로)
값 타입 복사
Member member = new Member();
member.setHomeAddress(address);
em.persist(member);
Address copyAddress = new Address(address.getCity(), address.getStreet, address.getZipcode); // 값 복사
Member member2 = new Member();
member2.setHomeAddress(copyAddress);
em.persist(member2);
객체 타입의 한계
// 기본 타입 (primitive type)
int a = 10;
int b = a; // 기본 타입은 값을 복사
b = 4; // a는 유지
// 객체 타입
Address a = new Address("Old");
Address b = a; // 객체 타입은 참조를 전달
b.setCity("New") // a, b 둘다 변경됨
불변 객체
int a = 10;
int b = 10;
// a == b true
Address address1 = new Address("서울시");
Address address2 = new Address("서울시");
// address1 == address2 false
address1.equals(address2);
public class Member extends BaseEntity {
...
@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<>();
}
@Embeddable
public class Address {
private String city;
private String street;
private String zipcode;
}
Member member = new Member();
member.setUsername("member1");
member.setHomeAddress(new Address("homeCity", "street", "zipcode"));
member.getFavoriteFoods().add("치킨");
member.getFavoriteFoods().add("족발");
member.getFavoriteFoods().add("보쌈");
member.getAddressHistory().add(new Address("old1", "street", "zipcode"));
member.getAddressHistory().add(new Address("old2", "street", "zipcode"));
em.persist(member);
Member findMember = em.find(Member.class, member.getId());
//homeCity -> newCity
Address a = findMember.getHomeAddress();
findMember.setHomeAddress(new Address("newCity", a.getStreet(), a.getZipcode())); // 새 값 타입을 만들어서 넣기
// 값 타입 컬렉션 수정
// 컬렉션 내부 치킨 -> 한식
findMember.getFavoriteFoods().remove("치킨");
findMember.getFavoriteFoods().add("한식");
// 값 타입 컬렉션 수정
// 컬렉션 내부 old1 -> newCity1
findMember.getAddressHistory().remove(new Address("old1", "street", "zipcode")); // remove 동작 시 equals가 동작하므로 지우려고자 하는 객체와 동일한 객체 입력해야 삭제됨
findMember.getAddressHistory().add(new Address("newCity1", "street", "zipcode"));
값 타입 컬렉션은 영속성 전이 + 고아 객체 제거 기능을 필수로 가진다고 볼 수 있음
값 타입 컬렉션의 제약사항 (중요)
값 타입 컬렉션 대안
/*
@ElementCollection
@CollectionTable(name = "ADDRESS", joinColumns =
@JoinColumn(name = "MEMBER_ID")
)
private List<Address> addressHistory = new ArrayList<>();
*/
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "MEMBER_ID")
private List<AddressEntity> addressHistory = new ArrayList<>();
@Entity
@Table
public class AddressEntity
@Id @GeneratedValue
private Long id;
private Address address; // address를 감싸준다
그래서 값 타입 컬렉션은 언제? -> 정말 추적이 필요하지 않는 단순한 값 처리할 때 (체크 박스 등)
정리