[JPA] 값 타입 컬렉션

·2024년 4월 16일
0

JPA

목록 보기
10/17
post-thumbnail

💡값 타입 컬렉션

  • 값 타입을 컬렉션으로 넣어서 쓰는 것을 의미
  • @ElementCollection, @CollectionTable 사용
    • @ElementCollection : 컬렉션 객체임을 JPA가 알 수 있게 해준다
    • @CollectionTable : 값 타입 컬렉션을 매핑할 테이블에 대한 역할을 지정하는 데 사용한다.
  • 데이터베이스는 컬렉션을 같은 테이블에 저장할 수 없다.
    • 일대다로 풀어서 별도의 테이블로 만들어야 한다.
  • 컬렉션을 저장하기 위한 별도의 테이블이 필요
    • 즉, 조인키가 필요하다

📗값 타입 컬렉션 사용

📌값 타입 저장 예제

좋아하는 음식들을 저장하는 favoriteFoods 주소내역을 저장하는 addressHistory 구현해보자.

member.java

...
//favoriteFoods
//값 타입 컬렉션 지정
@ElementCollection
@CollectionTable(name="FAVORITE_FOOD", joinColumns =
					@JoinColumn(name="MEMBER_ID"))	//조인할 컬럼명 지정
   					@Column(name="FOOD_NAME")		//컬럼명 지정
private Set<String> favoriteFoods = new HashSet<>();

//addressHistory
//값 타입 컬렉션 지정
@ElementCollection
@CollectionTable(name="ADDRESS", joinColumns = 
					@JoinColumn(name="MEMBER_ID"))
private List<Address> addressHistory = new ArrayList<>();
  • @ElementCollection : 컬렉션 객체임을 JPA가 알 수 있게 해준다. 엔티티가 아닌 값 타입, 임베디드 타입에 대한 테이블을 생성하고 1대다 관계로 다룬다.
  • @CollectionTable : 값 타입 컬렉션을 매핑할 테이블에 대한 역할을 지정하는 데 사용한다. 테이블의 이름과 조인정보를 적어줘야 한다.

값 저장

Member member1 = new Member();
member1.setUsername("member1");

member1.setHomeAddress(new Address("city1","Street1", "1000"));

member1.getFavoriteFoods().add("치킨");
member1.getFavoriteFoods().add("피자");
member1.getFavoriteFoods().add("족발");
    
member1.getAddressHistory().add(new Address("old1","Street1", "1000"));
member1.getAddressHistory().add(new Address("old2","Street2", "1000"));

//멤버만 persist
em.persist(member1);
  • member1객체만 persist했음에도 FAVORITE_FODD테이블과 ADDRESS테이블에도 컬렉션에 저장한 값이 들어가는걸 확인할 수 있다.

📌값 타입 조회 예제

//멤버만 불러와짐. 컬렉션타입 : 기본값은 지연로딩
System.out.println("==================== START ====================");
Member findMember = em.find(Member.class, member1.getId());
  • 컬렉션 값 타입의 @ElementCollection은 LAZY로 설정돼 있어 지연로딩으로 작동한다.
    • em.find()로 member를 호출하면 member의 값들만 호출된다.

📌값 타입 수정 예제

//setter 사용X. 사이드이펙트 생길수 있다.
//findMember.getHomeAddress().setCity("newCity");

// 치킨 -> 한식
member.getFavoriteFoods().remove("치킨");
member.getFavoriteFoods().add("한식");

// 주소변경
member.getAddressHistory().remove(new Address("oldCity", "street", "10000"));
member.getAddressHistory().add(new Address("newCity", "street", "10000"));
  • 컬렉션 값 타입을 수정할 때 setter를 사용하면 사이드 이펙트 발생 가능성이 있으므로 사용하면 안된다.
  • 값 타입 컬렉션 안의 데이터를 수정할 때는 일부만 수정하는 것이 아닌 데이터를 삭제 후 변경된 데이터 전체를 새로 추가해줘야 한다.

📗값 타입 제약사항

  • 값 타입은 엔티티와 다르게 식별자 개념이 없다.
  • 값은 변경하면 추적이 어렵다.
  • 값 타입 컬렉션에 변경 사항이 발생하면, 주인 엔티티와 연관된 모든 데이터를 삭제하고, 값 타입 컬렉션에 있는 현재 값을 모두 다시 저장한다.
  • 값 타입 컬렉션을 매핑하는 테이블은 모든 컬럼을 묶어서 기본 키를 구성해야 함 -> null 입력X, 중복 저장X
//값 타입 2개 add
member.setAddressHistory.add(new Address("oldCity1", "street", "10000");
member.setAddressHistory.add(new Address("oldCity2", "street", "10000");

member.getAddressHistory().remove(new Address("oldCity1", "street", "10000"));
member.getAddressHistory().add(new Address("newCity", "street", "10000"));
  • 값 타입 컬렉션에 Address 데이터가 2개 저장되어 있는 상황에서 기존에 있던 데이터 1개를 삭제하고 새로운 데이터를 추가해 주었다.
  • 우리가 기대하는 SQL은 delete 쿼리 1개와 insert 쿼리 1개이다.
  • 하지만 값 타입 컬렉션은 주인 엔티티와 연관된 모든 데이터를 삭제하고 다시 저장하기 때문에 Member에 연관된 oldCity1, oldCity2를 모두 삭제하고 oldCity2와 newCity를 insert 한다.
    ➡️따라서 delete 쿼리 1개와 insert 쿼리 2개가 DB로 나가게 된다.

📗값 타입 컬렉션 대안

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

AdressEntity.java

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

}

Member.java

@Entity
public class Member {
...
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "MEMBER_ID")
private List<AddressEntity> addressHistory = new ArrayList<>();
...
}
  • Address라는 값 타입을 감싸는 AddressEntity를 새롭게 만들고 저장하는 방식
  • cascade를 ALL, orphanRemoval을 true로 설정해 주면 주인 엔티티에 의존하면서 기존의 제약사항을 해결할 수 있다.
profile
백엔드 개발자를 꿈꿉니다 / 이전 블로그 : https://po-dadak.tistory.com/category

0개의 댓글

관련 채용 정보