Аргументы конструктора и проблема десериализации данных Spring
Использование Spring Data с MongoDB.
Проблема заключается в создании экземпляра бина, полученного из Mongo, потому что конструктор имеет некоторый аргумент, который на самом деле находится во вложенном объекте. Упорство не проблема. Извлечение
Структура класса, как показано ниже, где B вложена в A. A устанавливает B, создавая его экземпляр, используя один из его конструкторов Params.
Класс А
Class A{
int prop1;
B b;
@JsonCreator
public A( @JsonProperty int prop1, @JsonProperty int prop2){
this.prop1 = prop1;
this.b = new B(prop2);
}
}
Класс B, который вложен в A
Class B(){
int prop2;
@JsonCreator
public B(@JsonProperty int prop2){
this.prop2 = prop2;
}
}
Когда объект получен REST API в приведенной ниже форме Json:
{"prop1":"Hello1","prop2":"Hello2"}
Spring Controller получает его и корректно отображает на объект A. Поскольку Spring по умолчанию использует конструктор без аргументов, я использовал аннотацию jsoncreator на конструкторе arg, и он добавляется в MongoDB без каких-либо сует.
Данные хранятся в следующем формате в соответствии с правильной структурой компонента.
{"prop1":"Hello1","b":{"prop2":"Hello2"}}
Приходя к проблеме: ошибка возникает, когда я пытаюсь получить из Mongo. Это говорит:
org.springframework.data.mapping.model.MappingException: No property prop2
found on entity class A to bind constructor parameter to
Как я могу сказать SpringData использовать объект B, который содержит prop2, при получении его из SpringData? (Возможно, это не возможно)
Я подумал, что, возможно, добавление еще одного конструктора поможет, и я использую объект B в качестве одного из аргументов конструктора, как показано ниже:
public A(int prop1, B b){
......
......
}
Добавление еще одного конструктора работает с ObjectMapper, но опять же не с SpringData.
На этот раз выдается новое исключение:
org.springframework.data.mapping.model.MappingInstantiationException:
Failed to instantiate A using constructor NO_CONSTRUCTOR with
arguments.
Я проверил вышеупомянутую ошибку, где List используется / не используется, но я пробовал оба пути и не решил.
Обратите внимание: я использую объект B как вложенный, так как он содержит общие свойства, которые будут использоваться многими другими bean-компонентами (может быть абстрактным классом, но позже мне нужно будет попробовать это, но каким-то образом абстрактный класс кажется ограничивающим)
Как SpringData создает объект A?
1 ответ
Похоже на бестактность.
Должны были использовать лучшие термины в поиске. Нашел бы правильный ответ. На всякий случай, если кто-то сталкивается с этой проблемой...
Некоторые поиски привели меня к следующему вопросу:
Как именно spring-data-mongodb обрабатывает конструкторы при регидратации объектов?
Вышеупомянутый вопрос упоминает несколько вещей, которые дали мне решение. Ответ заключается в использовании другого конструктора для десериализации из SpringData в bean. Этот конструктор должен быть аннотирован:
@PersistenceConstructor
В примере в вопросе, если я использую второй конструктор с этой аннотацией, поиск работает.
@PersistenceConstructor
public A(int prop1, B b){
......
......
}
Итак, теперь у меня есть 2 конструктора, один с @jsoncreator, а другой с @PersistenceConstructor. Но добавим, что этот другой конструктор может быть закрытым и в том случае, если у вас нет для него другого использования, кроме как для извлечения из SpringData, и он не будет использоваться в коде кем-либо, таким образом оставляя вашу бизнес-логику нетронутой. Следовательно, ниже также будет работать:
@PersistenceConstructor
private A(int prop1, B b){
......
......
}