EP10. 값 타입
Web/JPA

EP10. 값 타입

엔티티 타입 vs 값 타입

 

엔티티

값을 변경해도 식별자로 인식 가능.

 

값을 변경하면 그냥 바뀜

 

값 타입의 종류

기본값 타입

 

  • int, double, Integer, Long, String
  • int는 공유가 안되는데 Integer 같은 래퍼 클래스는 참조로 가져가기 때문에 값을 변경하면 다른것도 바뀜 그렇지만 값이 변경이 안되어서 괜찮다.

 

임베디드 타입

  • 객체로 값타입들을 모은 타입 (복합 값 타입)
  • @Embeddable : 임베디드 타입을 정의 한 곳에. (기본 생성자를 만들어주고 사용해야한다.)
  • @Embedded : 임베디드 타입을 사용할 곳에
  • 임베디드 타입을 사용해도 매핑되는 테이블은 똑같다.
  • 정의되는 곳에 엔티티를 넣을 수도 있다.

  • 같은 값 타입을 중복해서 사용할 때는 @AttributeOverrides를 사용한다.
  • 같은 값타입을 쓰는 다른 엔티티에서 값타입을 수정하면 다른 엔티티도 바뀌게된다.
    그래서 값타입을 불변객체로 만들어야한다. -> Setter를 만들지 않는다.

 

 

 

 

값 타입 비교

동일성 비교 : 참조 값을 비교, ==

동등성 비교 : 인스턴스값 비교, equals()

 

equals() 사용할때는 equals()를 오버라이드 받아 구현해주어야한다.

(인텔리제이에서 만들어주는 오버라이드 equals()를 그대로 사용하면 된다.)

 

  1. command + N 
  2. equals() and hasgCode() 클릭

getter를 사용하도록 체크해야 프록시를 사용하더라도 equals() 가 작동한다.

 

 

값 타입 컬렉션

값 타입을 하나 이상 저장할 때 사용

 

값 타입 컬렉션은 각 값타입들을 다 묶어서 PK로 설정해주어야 한다.

 

 

 

값 타입 컬렉션 사용

 

  • @ElementCollection
  • @CollectionTable
    name : 테이블 이름 지정
    joinColumns = @JoinColumn(name = "MEMBER_ID") : FK 매핑
  • Embedded 타입이 아닌경우는 Column명 설정 가능
    @Column(name = "FOOD_NAME")

굳이 이렇게 사용하는 이유

 

데이터베이스에서는 컬렉션을 같은 테이블에 저장할 수 없다.

그래서 일 대 다 개념으로 사용하기 때문에 FK매핑이 필요해진다.

 

저장

 

코드

결과

값 타입들은 엔티티에 의존하기 때문에 em.persist(member); 한번으로도 값 타입들의 insert 쿼리들이 한번에 날아간다.

( = 값 타입 컬렉션도 지연 로딩 전략을 사용한다.)

 

값 타입 컬렉션의 지연로딩 전략

findMember를 생성할 시점에는 List, Set (컬렉션) 타입의 데이터를 가져오지 않았다가 사용시점에 insert쿼리가 날아가는 것을 볼 수 있다.

@ElementCollection의 default 가 fetch LAZY 이다.

 

 

 

 

값 타입 수정

findMember.getHomeAddress().setCity("newCity");

-> 안된다. side effect 발생.

 

Address homeAddress = findMember.getHomeAddress();
findMember.setHomeAddress(new Address("newCity", homeAddress.getStreet(), homeAddress.getZipcode()));

이런 식으로 값 타입을 새로 만들어서 넣어줘야한다.

 

 

 

값 타입 컬렉션 수정

 

Set

member.getFavoriteFoods().add("치킨");
member.getFavoriteFoods().add("족발");
member.getFavoriteFoods().add("피자");

findMember.getFavoriteFood() -> Set<String> 형식이다.

여기서 "치킨" 을 "한식"으로 바꾸려고 할때도 값 타입 수정에서의 원리를 적용해야한다.

왜냐하면 String도 값타입이기 때문에 "수정"을 못하는 "불변의 법칙"이 적용되어있기 때문이다.

 

수정방법

findMember.getFavoriteFoods().remove("치킨");
findMember.getFavoriteFoods().add("한식");

 

 

 

List

findMember.getAddressesHistory().remove(new Address("old1", "street1", "10000"));
findMember.getAddressesHistory().add(new Address("newCity1", "street1", "10000"));

new Address 자체를 넣어서 찾은다음에 삭제시키고, 새로 new Address를 넣어준다.

new Address 자체를 넣어 찾기때문에 equals와 hashcode가 중요해진다.

 

 

 

실무에서 값타입 컬렉션의 대안

일대다 관계 엔티티를 만들고 여기에 값 타입을 넣는다.

영속성 전이(Cascade) 와 orphanRemover를 사용해서 값 타입 컬렉션처럼 사용한다.

 

AddressEntity

package hellojpa;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@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);
    }

    public AddressEntity(Address address) {
        this.address = address;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }
}

 

Member

...
@OneToMany(cascade = ALL, orphanRemoval = true)
@JoinColumn(name = "MEMBER_ID")
private List<AddressEntity> addressesHistory = new ArrayList<>();
...


JpaMain

 

값 넣기

...
member.getAddressesHistory().add(new AddressEntity("old1", "street1", "10000"));
member.getAddressesHistory().add(new AddressEntity("old2", "street1", "10000"));
...

 

값 수정하기

 

... 방법 모르겠음 

 

 

 

정리

엔티티 타입 특징

  • 식별자가 필요하다
  • 생명주기 관리가 가능하다
  • 공유가 가능하다

 

 

 

값 타입 특징

  • 식별자가 없다
  • 생명주기를 엔티티에 의존한다.
  • 공유하지 않고 값을 복사해서 넣어야 한다.
    (이때 혹시 공유해 사용될 수 있어서 사이드이펙트를 방지하기 위해 불변객체로 만든다.)
  • 정말 값 타입이라고 판단될 때 사용한다.
    (식별자가 필요하고 값을 추적, 변경해야 한다면 엔티티로 만든다.)

'Web > JPA' 카테고리의 다른 글

EP11. JPQL소개, 기본문법  (0) 2021.02.19
CascadeType.REMOVE vs orphanRemoval = true  (0) 2021.02.13
EP9. 프록시와 연관관계 관리  (0) 2021.02.10
EP8. 고급 매핑  (0) 2021.02.09
EP7. 다양한 연관관계 매핑  (0) 2021.02.08