결론부터 말하자면,
- MongoDB에서 JPA의 상속관계 설정인 @Inheritance, @DiscriminatorColumn, @DiscriminatorValue는 적용되지 않는다.
- JAVA로 MongoDB에서 데이터 조회할 때, 자식 클래스 중 어떤 클래스와 매핑할 지 결정하는 것은 Document 내 저장되어 있는 _class이다. _class는 JAVA에서 데이터 저장 시, Document에 자동으로 같이 저장된다.
- _class로 구분하기 때문에, 자식 클래스의 클래스명은 변경하면 안된다. 변경 시, 기존 저장된 데이터를 매핑해서 가지고오지 못한다.
- @TypeAlias를 이용해 _class에 들어갈 값을 정해줄 수 있다.
JPA 상속관계 설정
JPA에서 상속 관계는 부모 엔터티에 @Inheritance로 조인 전략을 설정해주고, 어떤 자식 클래스와 매핑할 것인지 구분하기 위한 구분자 컬럼으로 @DiscriminatorColumn을 설정해준다. 그리고 자식 클래스에 @DiscriminatorValue를 넣어 DiscriminatorColumn에 들어갈 값을 지정해 준다.
자세한 내용은 아래글 참조.
https://developer-minji.tistory.com/85?category=1038052
이게 왜 되지?
클래스 구조
기능을 구현할 때 아래와 같은 클래스 구조로 데이터를 저장할 일이 있었다.
부모 클래스는 Item이고, @DiscriminatorColumn을 type으로 줬다.
자식 클래스는 Novel과 Movie가 있는데, @DiscriminatorValue를 각각 Book과 Moive로 줬다.
Essay라는 자식 클래스를 추가할 일이 생겼다. 근데, type은 Book으로 분류하고 싶다.
그래서 아래와 같이 Essay의 @DiscriminatorValue를 Novel과 같이 Book으로 지정했다.
@DiscriminatorValue는 자식 클래스를 구분하는 값이므로 값이 중복되면 안된다고 생각했는데, 데이터가 클래스 구분대로 저장되고 조회되었다.
왜 @DiscriminatorValue가 중복이 되었는데도 잘 되지? 라는 의문증이 생겼다.
소스
Controller
@RestController
@RequiredArgsConstructor
@RequestMapping("/inheritance")
public class InheritanceTestController {
private final InheritanceTestService inheritanceTestService;
@PostMapping
public ResponseEntity<Void> insertData(){
inheritanceTestService.insertData();
return ResponseEntity.ok().build();
}
@GetMapping
public ResponseEntity<List<Item>> getData(){
return ResponseEntity.ok().body(inheritanceTestService.getData());
}
}
Service
@Service
@RequiredArgsConstructor
public class InheritanceTestService {
private final ItemRepository itemRepository;
public void insertData() {
Novel novel = new Novel("소설 제목", "저자", "로맨스");
Essay essay = new Essay("에세이 제목", "저자", "일상");
Movie movie = new Movie("영화", "감독", "배우");
itemRepository.insert(novel);
itemRepository.insert(essay);
itemRepository.insert(movie);
}
public List<Item> getData() {
return itemRepository.findAll();
}
}
Repository
Item 클래스로 조회
@Repository
public interface ItemRepository extends MongoRepository<Item, String>{
}
Document 클래스 모델
부모 클래스
@Getter
@Setter
@Document(collection = "item")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) //상속 설정
@DiscriminatorColumn(name = "type") //type으로 자식 클래스 구분
public class Item {
@Id
private String id;
private String name;
public Item(String name) {
this.name = name;
}
}
자식 클래스
@Getter
@Setter
@Document(collection = "item")
@DiscriminatorValue("Book")
public class Novel extends Item{
private String author;
private String novelType;
public Novel(String name, String author, String novelType) {
super(name);
this.author = author;
this.novelType = novelType;
}
}
@Getter
@Setter
@Document(collection = "item")
@DiscriminatorValue("Book")
public class Essay extends Item{
private String author;
private String essayType;
public Essay(String name, String author, String essayType) {
super(name);
this.author = author;
this.essayType = essayType;
}
}
@Getter
@Setter
@Document(collection = "item")
@DiscriminatorValue("Movie")
public class Movie extends Item {
private String director;
private String actor;
public Movie(String name, String director, String actor) {
super(name);
this.director = director;
this.actor = actor;
}
}
GET 요청 결과
[GET] http://localhost:8080/inheritance
== Response Body
[
{
"id": "6281d8913a68c819b6634242",
"name": "소설 제목",
"author": "저자",
"novelType": "로맨스"
},
{
"id": "6281d8923a68c819b6634243",
"name": "에세이 제목",
"author": "저자",
"essayType": "일상"
},
{
"id": "6281d8923a68c819b6634244",
"name": "영화",
"director": "감독",
"actor": "배우"
}
]
실험
왜 @DiscriminatorValue가 중복되어도 각 자식 클래스에 맞게 매핑되어 결과가 나올까?
MongoDB의 데이터를 살펴봤는데, @DiscriminatorColumn으로 지정한 type 값이 저장되지 않고있었다.
type이 저장되지 않기 때문에, @DiscriminatorColumn을 가지고 자식 클래스를 구분하지 않는다는 생각이 들었다.
Item클래스와 자식 클래스 모두에서, @Inheritance와 @DiscriminatorColumn, @DiscriminatorValue를 제거했다.
그리고 데이터를 다시 저장하고 조회해보니, 제거 전이랑 동일한 결과가 나왔다.
@Inheritance와 @DiscriminatorColumn으로 구분하지 않는 것이 확실했다.
그럼 MongoDB는 자식 클래스를 무엇으로 구분하는 걸까?
MonboDB에 저장되는 _class라는 생각이 들었다.
그래서 MongoDB에서 _class를 자식 클래스와 다르게 변경해보았다.
클래스가 Novel인 것을 ChangeClass로 변경하였다.
다시 Get으로 결과를 조회해보니 Novel의 author와 novelType이 조회되지 않고, Item 클래스에 있는 정보만 매핑되어 나왔다. 이로써 _class가 어떤 자식 클래스와 매핑할지 결정해준다는 것을 알 수 있었다.
[GET] http://localhost:8080/inheritance
== Response Body
[
{
"id": "6281df4d568c887607932135",
"name": "소설 제목" // author와 novelType이 나오지 않음.
},
{
"id": "6281df4d568c887607932136",
"name": "에세이 제목",
"author": "저자",
"essayType": "일상"
},
{
"id": "6281df4d568c887607932137",
"name": "영화",
"director": "감독",
"actor": "배우"
}
]
_class로 구분하므로, 자식 클래스의 이름을 변경하면 데이터를 가지고 오지 못한다.
아래와 같이 데이터가 들어있을 때, Novel 클래스 이름을 Novel2로 변경하였다.
Novel에 해당하는 데이터가 나오지 않음을 확인할 수 있다.
따라서, 자식 클래스명을 변경하면 기존 저장된 _class 정보와 맞지 않으므로 데이터를 가져오지 못하게 된다.
운영 서버라면 자식 클래스명을 변경하지 않도록 주의해야한다.
[GET] http://localhost:8080/inheritance
== Response Body
[
{
"id": "6281e98b568c88760793213e",
"name": "소설 제목" // 자식 클래스의 이름 변경으로 인해 author와 novelType이 나오지 않음
},
{
"id": "6281e98b568c88760793213f",
"name": "에세이 제목",
"author": "저자",
"essayType": "일상"
},
{
"id": "6281e98b568c887607932140",
"name": "영화",
"director": "감독",
"actor": "배우"
}
]
@TypeAlias
Class 명이 변경되면 기존에 MongoDB에 저장된 데이터를 가지고 오지 못한다는 것에 위험성을 느꼈다.
나중에 누군가가 클래스명을 모르고 변경할 수 있기 때문이다.
@TypeAlias를 사용함으로써 _class에 어떤 정보를 넣어줄 지 정할 수 있다.
아래와 같이 클래스에 @TypeAlias를 지정해줌으로써
MongoDB의 _class가 변경된 것을 확인할 수 있었다.
조회 또한 UploadDocDocument클래스로 잘 가지고 오는 것을 확인했다.
'Database & ORM > MongoDB' 카테고리의 다른 글
MongoTemplate으로 Aggregation 사용하기 (0) | 2022.05.11 |
---|---|
MongoDB 선택 이유 및 MongoDB에서 RDB+MongoDB로 변경 (0) | 2022.04.19 |