tonykang22 / study

0 stars 0 forks source link

[JPA] 9장. 값 타입 #143

Open callmeaxxe opened 1 year ago

callmeaxxe commented 1 year ago

값 타입?

9.1 기본값 타입

@Entity
public class Member {
    ...
    private String name;
    private int age;
    ...
}

9.2 임베디드 타입(복합 값 타입)

JPA 에서는 새로운 값 타입을 직접 정의해서 사용하는 경우 임베디드 타입이라 지칭함

@Entity
public class Member {
    ...
    @Embedded Period workPeriod;    // 근무 기간
    @Embedded Address homeAddress;  // 집 주소
    ...
}
@Embeddable
public class Period {
    @Temporal(TemporalType.Date)
    java.util.Date startDate;

    @Temporal(TemporalType.Date)
    java.util.Date endDate;
}
@Embeddable
public class Address {
    @Column(name="city") //매핑할 컬럼 정의
    private String city;
    private String street;
    private String zipcode;
    ...
}

image

임베디드 타입과 테이블 매핑

image

@AttributeOverride : 속성 재정의

@Entity
public class Member {
    ...
    @Embedded
    Address homeAddress;    

    @Embedded
    @AttributeOverrides({
        @AttributeOverride(name="city", column=@Column(name="COMPANY_CITY")),
        @AttributeOverride(name="street", column=@Column(name="COMPANY_STREET")),
        @AttributeOverride(name="zipcode", column=@Column(name="COMPANY_ZIPCODE"))
    })
    Address companyAddress;
    ...
}

임베디드 타입과 null

9.3 값 타입과 불변 객체

값 타입 공유 참조

image

member1.setHomeAddress(new Address("OldCity");
Address address = member1.getHomeAddress();

address.setCity("NewCity"); // 회원1의 address 값을 공유해서 사용
member2.setHomeAddress(address);

값 타입 복사

image

member1.setHomeAddress(new Address("OldCity");
Address address = member1.getHomeAddress();

// 회원1의 address 값을 복사해서 새로운 newAddress 값을 생성
Address newAddress = address.clone();

newAddress.setCity("NewCity");
member2.setHomeAddress(address);

9.4 값 타입의 비교

9.5 값 타입 컬렉션

@Entity
public class Member {
    ...
    @Embedded
    private Address homeAddress;

    @ElementCollection
    @CollectionTable(name = "FAVORITE_FOOD", 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>();
    ...
}

image

값 타입 컬렉션 사용 - 조회

//SQL: SELECT ID, CITY, STREET, ZIPCODE FROM MEMBER WHERE ID = 1
Member member = em.find(Member.class, 1L); //1. member

//2. member.homeAddress
Address homeAddress = member.getHomeAddress();

//3. member.favoriteFoods
Set<String> favoriteFoods = member.getFavoriteFoods(); //LAZY

//SQL: SELECT MEMBER_ID, FOOD_NAME FROM FAVORITE_FOODS WHERE MEMBER_ID = 1
for (String favoriteFood : favoriteFoods) {
    System.out.println("favoriteFood = " + favoriteFood);
}

//4. member.addressHistory
List<Address> addressHistory = member.getAddressHistory(); //LAZY

//SQL: SELECT MEMBER_ID, CITY, STREET, ZIPCODE FROM ADDRESS WHERE MEMBER_ID = 1
addressHistory.get(0);
  1. member: 회원과 임베디드 값 타입인 homeAddress 를 함께 조회
  2. member.homeAddress: 1번에서 회원 조회시 같이 조회
  3. member.favoriteFoods: LAZY 로 설정해서 실제 컬렉션을 사용할 때 SELECT SQL 1번 호출
  4. member.addressHistory: LAZY 로 설정해서 실제 컬렉션을 사용할 때 SELECT SQL 1번 호출

값 타입 컬렉션 사용 - 수정

Member member = em.find(Member.class, 1L);

//1. 임베디드 값 타입 수정
member.setHomeAddress(new Address("새로운도시", "신도시1", "123456"));

//2. 기본값 타입 컬렉션 수정
Set<String> favoriteFoods = member.getFavoriteFoods();
favoriteFoods.remove("탕수육");
favoriteFoods.add("치킨");

//3. 임베디드 값 타입 컬렉션 수정
List<Address> addressHistory = member.getAddressHistory();
addressHistory.remove(new Address("서울", "기존 주소", "123-123"));
addressHistory.add(new Address("새로운도시", "새로운 주소", "123-456"));
  1. 임베디드 값 타입 수정: Address 는 Member 테이블의 필드중 일부. 따라서 Member 테이블이 업데이트 된다
  2. 기본값 타입 컬렉션 수정: 수정의 대상이 되는 기본값을 제거한 뒤 새로 추가한다
  3. 임베디드 값 타입 컬렉션 수정: 값 타입은 불변해야 한다. 따라서 컬렉션에서 기존 주소를 삭제하고 새로운 주소를 추가한다(equals, hashcode 반드시 구현해야 함)

값 타입 컬렉션 제약사항

DELETE FROM ADDRESS WHERE MEMBER_ID=100
INSERT INTO ADDRESS (MEMBER_ID, CITY, STREET, ZIPCODE) VALUES (100, ... )
INSERT INTO ADDRESS (MEMBER_ID, CITY, STREET, ZIPCODE) VALUES (100, ... )
@Entity
public class AddressEntity {
    @Id
    @GeneratedValue
    private Long id;

    @Embedded
    Address address;
}
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "MEMBER_ID")
private List<AddressEntity> addressHistory = new ArrayList<AddressEntity>();

9.6 정리

엔티티 타입 vs 값 타입

엔티티 타입

값 타입

값 타입은 값 타입이라 판단될 때만 사용해야 한다. 식별자가 필요하고 지속해서 값을 추적하고 구분하고 변경해야 한다면 그것은 값 타입이 아닌 엔티티다.

식별자 (id) 값이 없다. 값 타입일까?

식별자 (id) 값이 없다. 값 타입일까?