-
[Spring JPA] @OneToMany 에서 @MapsId 사용하기Back End/Spring Data JPA 2022. 1. 24. 22:44
>> @OneToMany에서 @MapsId 올바르게 사용하는법 보러가기
>> @OneToOne에서 @MapsId 로 컬럼갯수 1개 줄여보기
@MapsId 에 관하여...
@MapsId는 FK를 PK로 지정할 때 사용하는 어노테이션이다.
이 말은 즉슨, @MapsId를 통해 엔티티의 PK를 FK로 매핑한다면, 데이터베이스 컬럼하나를 줄일 수 있다는 이야기이다...!
@MapsId를 검색하다가 @OneToMany 와 같이 검색어가 자동완성되기에, 이거 되는거구나!! 대박인걸!! 하고 웹상에 존재하는 예시코드를 따라해보았다.
결론부터 말하면, @OneToMany에서의 @MapsId 사용은 PK가 복합키일때만 사용가능하며, 위의 데이터베이스 컬럼하나를 줄일 수 있는 효과는 없다.
@OneToOne에서는 @MapsId 사용으로 @MapsId 를 사용하지 않았을 때, 디폴트로 생성되는 조인컬럼 필드를 하나 줄일 수 있다.
주의할점은, @OneToMany에서 @MapsId를 사용할때 PK가 복합키가 아니더라도 컴파일오류 및 DDL 오류는 발생하지 않는다.
오히려 멋지게 잘 동작한다. 다만 데이터 삽입시 PK를 지정하라는 오류 문구를 내뱉는다.
@MapsId 를 사용한 올바르지 않은 코드 (삽질)
바보같은 나는 또 삽질을 하였다.
이러한 상상을 했다.
DDD(Domain-Driven-Development) 느낌으로 설계를 하고싶어..!!!!
그러므로 상위엔티티에서 하위엔티티들을 관리할꺼야...!!!
하위엔티티 테이블 필드를 최대한 줄이고 싶어...!!!
하위엔티티의 단일 PK를 FK로 지정하면 디폴트로 생성되던 조인칼럼 하나 줄일 수 있겠구나!!!
=> Hibernate가 ORM인 이상 진짜 진짜 진짜 말도 안되는 소리다.
=> 물론 PK를 복합키로 사용하고 있다면 말이 되긴 한다.
DB에서 PK란 Unique한 값이며 Index로 지정되는 값이다. 이로인해 PK로 조회시 검색성능이 높아진다.
@OneToMany의 상황에서 단일 PK를 FK로 지정하는 것 자체가 모순이다.
하나의 부모를 가르키는 여러개의 자식이 있을건데 전혀 Unique하지 않다.
아래는 이 사실을 깨닫기 전에 필자가 작성한 코드이다. (개인기록용)
@OneToMany에서 단일 PK를 @MapsId로 매핑해보자!
# Item 엔티티 (Tags 를 OneToMany로 매핑중)
@Inheritance(strategy = InheritanceType.JOINED) @DiscriminatorColumn @Getter @AllArgsConstructor @NoArgsConstructor @ToString(callSuper = true) @Entity public class Item extends BaseEntityWithId { protected Long itemId; //FolderId & BookmarkId protected String name; protected Long parentId; protected BigDecimal visitCount; protected Long userId; @OneToMany(fetch = FetchType.LAZY, mappedBy = "item", cascade = CascadeType.ALL) protected List<Tag> tags; public void setTags(List<Tag> tags){ this.tags=tags; } }
# Tag 엔티티 (Item을 @ManyToOne으로 매핑중)
@Entity @AllArgsConstructor @Builder @NoArgsConstructor @Getter @ToString public class Tag { @Id @Column(name="item_id") private Long itemId; private String tag; @MapsId("item_id") @ManyToOne(fetch = FetchType.LAZY,optional = false) @JoinColumn(name= "item_id") @ToString.Exclude Item item; public void setItem(Item item){ this.item = item; } }
# DDL 실행결과
원하는대로 tag 테이블에서는 FK로 생성되는 다른 컬럼이 없다..!
하지만 저렇게 테이블 매핑은 되지만, Item.setTags() 와 Tags.setItem()으로 연관관계를 묶은 후 Insert를 하면 오류가 발생한다.
오류의 표면적이유는 " PK를 Manual 하게 지정하셔야 되는데 하지 않으셨습니다. " 인데...
생각해보면 당연하게도, Tag의 ItemId 를 PK로 지정했고, 그로 인해 PK는 Unique해야하는데,
일대다 관계기 때문에 Item의 PK를 Tag의 ItemId로 지정한다면 PK는 Unique해지지 못하므로
Hibernate가 @MapsId의 PK 주입을 허용하지 않는 것이다.
그럼 왜 오류문구가 " @MapsId를 잘못사용하셨습니다 " 가 아닐까?
실제 DB 에서는 PK 없이 테이블을 구성할 수 있다고 한다.
그리하여 Hibernate는 ORM 의 기능인 테이블에 매핑을 하기위한 @MapsId 는 동작을 허용하지만,
Insert 때 @MapsId로 연결된 컬럼을 자동삽입하는 기능은 모순되기에 동작을 허용하지 않는 것이다.
그리고 그 동작이 허용되지않으니, @GeneratedValue 가 아닌 PK는 값을 가지지 않게되고
그로 인해 PK를 Manual하게 지정해달라고 오류메세지를 띄우게 되는 것이다.
Hibernate의 움직임의 기초인 Proxy는 PK를 기반으로 움직이기 때문에
DB에서 PK 없이 테이블을 구성했을 때 매핑할 수 있는 방법 또한 아래와 같이 임의로 PK를 지정하는 방법이다.
PK없이 테이블을 구성했을 때, DB에서도 삭제시에 여러가지 아이템이 지워질 수 있다는 오류문구를 띄우기도 한다.
결국은 칼럼1개를 절약하기 위해 PK 없이 테이블을 구성하는 것은 구성원들의 실수로 데이터가 날라갈 수도 있는 상황을 만들 수 있기에 지양하는 것이 옳다.
칼럼 1개의 Trade-off 비용은 JPA에서 적용의 까다로움 + 유지보수 + 데이터손실의 위험이다.
OneToMany 에서 @MapsId 를 올바르게 사용하기
복합키를 만들어서 매핑한다.
복합 PK의 검색성능에 대해서는 아직 공부가 덜되서 확답을 할 수 없다.
위와 같이 테이블구조를 만들면 PK가 중복되지 않으므로 @MapsId의 필드자동주입 기능도 활성화 된다.
근데 언제 이런 구조를 가져가야하는지는 경험도 없고 지식도 없다... (추후 공부 필요)
# Tag 엔티티
@Entity @AllArgsConstructor @Builder @NoArgsConstructor @Getter @ToString @IdClass(CompositeId.class) // 복합키설정 public class Tag { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Id @Column(name="item_id") private Long itemId; public class CompositeId implements Serializable{ // 복합키는 Serializable private Long id; private Long itemId; } private String tag; @MapsId("item_id") @ManyToOne(fetch = FetchType.LAZY,optional = false) @JoinColumn(name= "item_id") @ToString.Exclude Item item; }
728x90'Back End > Spring Data JPA' 카테고리의 다른 글
[Spring JPA] 복합키(PK)의 성능을 알아보자! (0) 2022.01.26 [Spring JPA] @OneToOne 에서 @MapsId 를 이용해 컬럼갯수를 줄여보자! (0) 2022.01.26 [Spring JPA] referencedColumnName 사용시 주의점 (Feat. 프록시) (0) 2022.01.21 [Spring JPA] 상속관계 매핑의 문제점 (@Inheritance) (0) 2022.01.19 [Spring JPA] 슈퍼-서브타입 관계 모델링 (상속관계 매핑) (0) 2022.01.14