@ElementCollection의 개념과 주의할 점!

김승환·2023년 11월 30일
0

Spring

목록 보기
1/3
post-thumbnail

@ElementCollection

값 타입을 컬렉션에 담아서 사용할 때, 해당 컬렉션을 값 타입 컬렉션이라 합니다.
@OneToMany 처럼 엔티티를 컬렉션으로 사용하는 것이 아닌, Integer, String, 임베디드 타입 같은 값 타입을 컬렉션으로 사용하는 것입니다.

하지만 RDB에는 컬렉션을 담을 수 있는 구조가 없어 별도의 테이블을 만들어서 저장하게 되는데요.
이때 값 타입 컬렉션은 개념적으로 보면 1대 N 관계를 사용하게 됩니다.

그리고 값 타입을 저장하는 테이블은 값 타입을 소유한 엔티티의 기본 키와 모든 값 타입 필드를 묶어서 PK로 사용하며, 엔티티의 기본 키를 PK겸 FK로 사용합니다.
위의 내용이 제가 이 포스트를 작성하게 된 이유입니다. 기억해주세요!

아래는 제가 실제로 사용한 @ElementCollection 예시입니다!

@CollectionTable

@CollectionTable은 값 타입 컬렉션을 매핑할 테이블에 대한 정보를 지정하는 역할을 수행합니다
사용하지 않는다면 기본값을 이용해서 매핑한다고 하네요.
저는 기본값으로 Shop이라는 엔티티에서 detailPhotoList라는 이름으로 값 타입 컬렉션을 생성하니 shop_detail_photo_list라는 테이블 명으로 생성이 되었습니다.

@ElementCollection에서 주의할 점

SOPT라는 창업 연합 동아리에서 클라이언트, 디자이너, 서버파트가 함께 프로젝트를 만들어보는 합동세미나에서 tabling이라는 서비스를 구현하게 되었습니다.

tabling에서는 매장 엔티티에서 매장 상세 사진을 저장하는 url을 @ElementCollection으로 구현하고자 하였는데요.
깃허브에서 이슈 생성 창에 들어가 사진을 붙여넣고, 생성된 url을 사용해 데이터베이스에 더미 데이터를 넣는 방식으로 생성하게 되었습니다.

위의 이슈 생성 창에서 생성된 url을 아래 사진처럼 더미데이터로 넣어주었습니다.

하지만 이렇게 API를 개발하고, 이미지를 넣어주고 일주일이 지나자 클라이언트 개발자 분께서 이미지가 뜨지 않는다고 확인을 부탁드린다는 말씀을 해주셨습니다.
확인을 해본 결과 사진 url을 다시 들어가보니 404 Not Found error가 뜨는 것을 확인할 수 있었고, 깃허브 이슈 창에서 생성한 이미지 url의 유효기간인 일주일이 지나서 404 error가 나는 것으로 판단하였습니다.
따라서 임시로 한 url로 모든 행의 url을 대체해주었습니다.
하지만 이후에 더미 데이터를 다양하게 작성하기 위해 DataGrip으로 url을 수정하려 했지만

위의 데이터를 아래처럼 변경하였을 때 에러가 발생하게 되었습니다.

여러 번의 삽질을 해보다가 쿼리를 한 번 확인해보게 되니

tabling-server-database> UPDATE `tabling-server-database`.shop_detail_photo_list t SET t.url = '변경한 url' WHERE t.shop_shop_id = 11 AND t.url LIKE '새로운 url' ESCAPE '#'

위와 같은 쿼리가 나가는 것을 확인할 수 있었습니다.

@ElementCollection으로 생성한 식별자가 존재하지 않아 SQL의 LIKE문을 통해 행을 구분하게 되는데, 에러가 발생한 67행, 68행을 보면 shop_id 도 11로 같고, url도 "새로운 url"이라는 이름으로 같은 url을 사용하니 SQL 입장에서는 67행과 68행을 구분할 수 없어 이 중 한 개만 수정한다는 것이 불가능했던 것입니다.
따라서 어쩔 수 없이 DELETE 쿼리를 날려 행을 삭제하고, 다시 url을 생성할 수 밖에 없었으며 이는 실제 서비스였다면 DB를 날린다는 아찔한 경험일 것 입니다.

이 경험을 통해 @ElementCollection을 좀 더 신중하게 사용해야 한다는 점에 대해서 다시 생각하고, SQL에서의 식별자의 중요성을 깨달을 수 있었던 경험이었습니다 :)

@ElementCollection의 특징

  • 연관된 부모 Entity 하나에만 연관되어 관리됩니다. (부모 Entity와 독립적으로 사용이 불가능합니다.)
  • 항상 부모와 함께 저장되고 삭제되므로 cascade 옵션은 제공하지 않습니다. (cascade = ALL입니다.)
  • 부모 Entity Id와 추가 컬럼(basic or embedded 타입)으로 구성됩니다.
  • 기본적으로 식별자 개념이 없으므로 컬렉션 값 변경 시, 전체 삭제 후 새로 추가합니다.

@ElementCollection의 제약

  • 값 타입은 엔티티와 다르게 식별자 개념이 없어 값을 변경하면 추적이 어렵다 => 방금 언급 한 내용!
  • 값 타입 컬렉션에 변경 사항(저장, 삭제)이 발생하면, 소유하는 엔티티와 연관된 모든 데이터를 삭제하고, 현재 남아있는 값을 모두 다시 저장한다.
  • 값 타입 컬렉션을 매핑하는 테이블은 모든 컬럼을 묶어서 기본키를 구성해야 한다. => null 입력 X, 중복 저장 X

값 타입 컬렉션의 대안

따라서 이런 제약들이 존재하여 값 타입 컬렉션 대신에 일대다 관계를 고려하는 것이 좋다고 합니다.
일대다 엔티티를 만들고, 엔티티에서 값 타입을 사용합니다.
cascade와 고아 객체 제거를 설정해서 값 타입 컬렉션처럼 사용할 수 있습니다.

0개의 댓글