spring/JPA

(JPA) 값 타입 컬렉션

ri5 2021. 7. 6. 21:53

값 타입 컬렉션


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

• @ElementCollection, @CollectionTable 사용

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

• 컬렉션을 저장하기 위한 별도의 테이블이 필요함

 

- MEMBER

@Entity
public class Member{
    @Id
    @GeneratedValue
    @Column(name="MEMBER_ID")
    private Long id;

    @Column(name="USERNAME")
    private String name;

    @Embedded
    private Period workPeriod;

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

 

- 저장

Member member = new Member();
member.setName("member1");
member.setHomeAddress(new Address("homeCity","street1","10000"));

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

member.getAddressHistory().add(new Address("city2","street2","10001"));
member.getAddressHistory().add(new Address("city3","street3","10003"));

em.persist(member);

- ADDRESS

CITY STREET ZIPCODE MEMBER_ID
city2 street2 10001 1
city3 street3 10003 1

 

- 조회

Member findMember = em.find(Member.class,member.getId());
List<Address> addresses = findMember.getAddressHistory();
Address a = findMember.getHomeAddress();

※ 값타입 컬렉션도 지연 로딩 전략 사용

 

- 수정

값타입은 불변객체이기 때문에 값을 지우고 새로 값을 넣어야한다.

findMember.getAddressHistory().remove(new AddressEntity("city2","street2","10001"));
findMember.getAddressHistory().add(new AddressEntity("newCity2","street2","10001"));

em.persist(findMeber);

참고: 값 타입 컬렉션은 영속성 전이(Cascade) + 고아 객체 제거 기능을 필수로 가진다고 볼 수 있다.

 

값 타입 컬렉션 제약 사항

• 값 타입은 엔티티와 다르게 식별자 개념이 없다.

• 값은 변경하면 추적이 어렵다.

• 값 타입 컬렉션에 변경 사항이 발생하면, 주인 엔티티와 연관된 모든 데이터를 삭제하고, 값 타입 컬렉션에 있는 현재 값을 모두 다시 저장한다.

• 값 타입 컬렉션을 매핑하는 테이블은 모든 컬럼을 묶어서 기본 키를 구성해야 함: (구분해주는 식별자가 따로 없음)

null 안됨, 중복 저장 안됨

 

값 타입 컬렉션 대안

• 실무에서는 상황에 따라 값 타입 컬렉션 대신에 일대다 관계를 고려

• 일대다 관계를 위한 엔티티를 만들고, 여기에서 값 타입을 사용

• 영속성 전이(Cascade) + 고아 객체 제거를 사용해서 값 타입 컬렉션 처럼 사용 

 

example

- AddressEntity

@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 AddressEntity() {

    }
}

- Main

// 저장
ember member = new Member();
member.setName("member1");
member.setHomeAddress(new Address("homeCity","street1","10000"));

member.getAddressHistory().add(new AddressEntity("city2","street2","10001"));
member.getAddressHistory().add(new AddressEntity("city3","street3","10003"));

em.persist(member);
//초기화
em.flush();
em.clear();

//조회
Member findMember = em.find(Member.class,member.getId());

// 수정
findMember.getAddressHistory().remove(new AddressEntity("city2","street2","10001"));
findMember.getAddressHistory().add(new AddressEntity("newCity2","street2","10001"));