2020. 2. 3. 21:54ㆍWeb/Spring
기본적으로 MongoDB는 ObjectId라는 유니크한 primary id를 갖는다. 하지만 @Id 어노테이션을 특정 Class로 매핑시키기 위한 방법은 없을까? 예를 들어 아래와 같은 상황이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
@Document
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DocumentData {
@Id
private CustomId id;
private String value;
@Data
@AllArgsConstructor(staticName = "of")
@NoArgsConstructor
public static class CustomId implements Serializable {
private String idPrefix;
private String idDelemeter;
}
}
|
cs |
DocumentData라는 Collection이 있고, 해당 Collection에는 ObjectId 타입이 아닌 CustomId 오브젝트 타입의 Id를 넣고 싶은 것이고, 실제로 Id는 내부적으로 String 타입이며 idPrefix + idDelemeter 라는 스트링으로 매핑하고 싶다. 즉, CustomId("prefix", "!"); 로 생성된 오브젝트가 있고 이것이 실제로 Mongodb에 들어가면 _id : "prefix!" 인 형태로 저장이 하고 싶은 것이다. 이럴때는 어떻게 해야할까?
1
2
3
4
5
6
7
8
9
10
11
|
@Bean
public MongoCustomConversions customConversions() {
return new MongoCustomConversions(Collections.singletonList(new CustomIdConverter()));
}
public static class CustomIdConverter implements Converter<DocumentData.CustomId, String> {
@Override
public String convert(DocumentData.CustomId source) {
return source.getIdPrefix() + source.getIdDelemeter();
}
}
|
cs |
위와 같이 MongoCustomConversions를 Bean으로 등록하면 특정 오브젝트 타입이 @Id로 Mapping 되어 있을 때, 구현한 Converter 내용에 따라 적절히 _id 값이 들어가게 된다. 위의 구현은 CustomId 오브젝트 타입이 @Id로 매핑되어 있을 때, idPrefix와 IdDelemeter를 조합하여 String Type의 _id 값을 만들어 낸다. 그리고 기존의 ObjectId로 매핑한 @Id도 그대로 사용가능하다.
1
2
3
4
5
6
7
8
|
public interface MongoRepositoryImpl extends ReactiveMongoRepository<DocumentData, DocumentData.CustomId> {}
@PostMapping("/mongo")
public Mono<DocumentData> save(){
DocumentData data = new DocumentData(DocumentData.CustomId.of("prefix","!"), "value");
mongoRepository.save(data);
return mongoRepository.save(data);
}
|
cs |
실제로 간단히 API를 만들어 요청을 보내보면 아래와 같이 데이터가 삽입되어 있다.
1
2
3
|
_id : "prefix!"
value : "value"
_class : "com.webflux.mongoexam.DocumentData"
|
cs |
실제 필자도 내부적으로 자체적인 복잡한 ID 체계를 가져가기 위해 MongoCustomConversions를 빈으로 등록하여 사용하고 있다.
추가적으로 하나더 이슈가 있다. 만약에 특정 커스텀 클래스를 @Id class로 사용하려면 해당 클래스가 반드시 Serializable을 구현하는 클래스이어야한다! 이 이슈를 발견하게 된 것은 ReactiveMongoRepository의 findById를 사용할때 였다. 아이디클래스가 Serializable을 구현하지 않으면 예외를 내뱉는다.
{
logEvent: "java.lang.ClassCastException",
errorMsg: "class xxx cannot be cast to class java.io.Serializable (xxx is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @2c4eab42; java.io.Serializable is in module java.base of loader 'bootstrap')"
}