JAVA

[JAVA] 값 타입 컬렉션

응디 2022. 1. 13. 17:28
  • 값 타입을 하나 이상 저장할 때 사용
  • @ElementCollection, @CollectionTable 사용
  • 데이터베이스는 컬렉션을 같은 테이블에 저장 할 수 없다.
  • 컬렉션을 저장하기 위한 별도의 테이블이 필요

 

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());

// 값 타입은 데이터 수정 시 아예 새로 넣어야한다. 변경하려는 값만 변경 하면 X(통째로 갈아끼워야함 update X )
// 만약 주소를 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("떡볶이");

// remove은 기본적으로 equals를 사용학기 때문에 비교값을 완전 똑같이 넣어줘야 한다.
findMember.getAddressHistory().remove(new Address("old1","street", "10000"));
findMember.getAddressHistory().add(new Address("newCity1","street", "10000"));

 

값 타입 컬렉션 사용

  • 값 타입 컬렉션도 지연 로딩 전략을 사용한다.
  • 값 타입 컬렉션은 영속성 전에(Cascade) + 고아 객체 제거 기능을 필수도 가진다고 볼 수 있다.

 

제약사항

  • 값 타입은 엔티티와 다르게 식별자 개념이 없다 → 따라서 값을 변경하면 추적이 어렵다.
  • 값 타입 컬렉션에서 변경사항이 생기면, 주인 엔티티와 관련 있는 모든 데이터를 삭제하고 다시 저장한다.
  • 값 타입 컬렉션을 매핑하는 테이블은 모든 컬럼을 묶어서 기본키를 구성해야 함
  • null 입력 X, 중복 X → 값 타입은 pk가 없고 딱히 pk로 지정해줘야하는 값도 없기 때문에 전체를 기본키로 구성한다.

값 타입 컬렉션 대안

  • 실무에서는 값 타입 컬렉션 대신에 일대다 관계를 고려
  • 일대다 관계를 위한 엔티티를 만들고, 여기에서 값 타입을 사용
  • 영속성 전이(Cascade) + 고아 객체 제거 를 사용해서 값 타입 컬렉션 처럼 사용

ex ) AddressEntity.java

@Entity
@Getter
@Table(name = "address")
public class AddressEntity {

    @Id @GeneratedValue
    private Long id;

    private Address address;


    public AddressEntity() {
    }

    public AddressEntity(String city, String street, String zipcode) {
        this.address = new Address(city, street, zipcode);
    }


}

 

Member.java

@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)  //cascate, 고아객체제거
@JoinColumn(name = "MEMBER_ID")
private List<AddressEntity> addressHistory = new ArrayList<>();

정리

  1. 값 타입은 정말 값 타입이라고 판단될 때만 사용
  2. 엔티티와 값 타입을 혼동해서 엔티티를 값 타입으로 만들면 안됨
  3. 식별자가 필요하고, 지속해서 값을 추적, 변경 해야 한다면 그것은 엔티티

'JAVA' 카테고리의 다른 글

[JAVA] JPA 조인과 서브쿼리  (0) 2022.01.18
[JAVA] 프로젝션과 페이징  (0) 2022.01.13
[JAVA] 값 타입과 불변객체  (0) 2022.01.11
[JAVA] 임베디드 타입(복합값)  (0) 2022.01.11
[JAVA] 영속성 전이(CASCADE) + 고아객체  (0) 2022.01.06