CH07 고급 매핑
Last updated
Last updated
상속 관계를 지닌 엔티티를 어떤 식으로 Relational 에 구현할지, 다르게 말하면 Object에 매핑을 어떤식으로 설정할지에 관한 내용이다.
이와 같은 Object 관계가 있다고 했을 때 이를 Relational 에 구현할 방법은 여러가지이다.
물품이라는 단일 테이블 하나로 데이터를 관리한다.
물품이라는 주 테이블을 두고 음반, 영화, 책 각각 테이블을 만들어서 각자 물품의 주키를 외래키로 갖는다.
음반, 영화, 책 각각의 테이블을 따로 만든다.
책에서는 위 세 가지 각각의 방법에 대해서 어떻게 구현할지, 각 방법의 장단점은 무엇인지에 관해서 다루고 있다. 각 전략마다 사용되는 주요 어노테이션은 아래와 같다.
하나의 테이블에서 DTYPE 과 같은 종류 구분 컬럼을 두고 관리하는 것인데, 테이블이 일단 정규화되지 않아서 음반, 영화, 책 각각의 경우마다 사용하지 않는 컬럼들이 혼재하게 된다.
내가 실무에서 사용하던 단일 테이블 전략은 상속 관계를 그대로 살리기 보다는 ITEM 이라는 하나의 개념으로 인식했던 것이고, 여기서 소개되고 있는 단일 테이블 전략은 아래와 같은 코드로 JPA 의 지원을 받아 위와 같은 테이블을 구성하는 것이다. join 을 하지 않아도 되니까 성능상 이점이 크고, 연관 관계도 따로 없으니 복잡도도 줄어든다.
내가 주로 사용했던 방법은 단일 테이블 내에 type 을 구분짓는 컬럼을 두고 각 엔티티 고유의 내용은 json 으로 특정 컬럼에 넣어놨다가 꺼낼때 objectMapper 로 매핑시켜서 사용하는 식이었다. 혹은 attribute1, attribute2 와 같은 범용적 네이밍의 컬럼을 두고 여기에 각 엔티티 고유의 데이터를 넣기도 하였다.
결국 핵심은 공통되는 컬럼이 있고, 각 엔티티 고유의 컬럼이 나뉘어져서 문제인 것이다. 즉, 분명하게 다른 세 가지 엔티티가 결국 상위의 추상적 단위인 '물품' 이라는 단위에 속한다는 것이다. 그래서 이렇게 여러 방법이 떠오르는 것인데 조인 전략의 경우 공통 컬럼들은 하나의 테이블에서 관리하고 그렇지 않은 고유의 컬럼들은 각자의 테이블에 관리하는 방법이다.
단일 테이블 전략과는 다르게 어쨌든 조회시 join 이 들어가서 성능상 단일 테이블 전략보다 불리하다. 대신 데이터가 확실하게 정규화가 되어있다는 장점이 있다. 아래는 예시 코드이다.
UNION SQL이 필요해서 성능상 비효율적이다. 책에서도 비추천하는 방법이다.
이건 아래와 같이 공통되는 컬럼이 있을때 사용하는 어노테이션이다.
개인적인 경험부터 남기자면 실무에서 위 어노테이션을 사용했었는데, 관리자에서 엑셀로 추출해야 할 때 리플랙션을 사용하는 라이브러리를 이용해서 처리해야 했어서 코드가 더 복잡해졌던 경험이 있다. 불가능한 건 아닌데 코드를 공통화해서 리플랙션을 이용해서 해당 엔티티의 필드 명들을 가지고 값을 가져오도록 처리한 부분이 있었는데 상속관계로 한번 더 찾아야 해서 공통화 해둔 코드를 사용할 수 없었다.
createdAt 등 어느 엔티티나 있을법한 공통되는 컬럼이 당연히 존재하는데 그만큼 그냥 복붙하면 간단하게 해결되는 부분인데 굳이 이걸 써야하나 싶기도 하다.
식별, 비식별 관계 정의와 함께 복합키를 사용하는 예제가 쭉 나와있는데 개인적으로 실무에서 식별 관계를 이용한 복합키를 쓸 일이 없었거니와 안쓰는 것이 더 좋다고 판단하기에 이 내용을 가볍게 읽기만 했다. 책에도 정리가 되어 있는데, 식별 관계를 사용함에 있어 안좋은 점들을 아래에 정리한다.
기본키를 자식 테이블로 전파하면서 자식 테이블의 기본 키 컬럼이 늘어난다. 조인시 SQL 이 복잡해지고 쓸데없이 인덱스가 더 걸리게 된다.
식별 관계의 기본 키로 비즈니스 의미가 있는 키를 자식 테이블로 전파하게 되었는데, 비즈니스가 변경되는 경우 수정 비용이 너무 크다. 변화에 유연하지 못하게 된다.
그냥 마음 편하게 대리 키를 사용하자.
이어서 브릿지테이블(꼭 다대다가 아니어도), 엔티티 하나에 여러 테이블 매핑에 관한 내용이 나오는데 개인적인 경험상 사용도가 떨어지거니와 특별히 정리할 내용이 없어서 생략한다.