#java #spring #spring-boot
#java #spring #spring-boot
Вопрос:
Я в начале изучения Spring. Я хочу написать серверную часть, которая сохраняет рецепты для приложения cookbook. Поэтому имейте «Рецепт» этой модели:
@Document(collection = "recipes")
public class Recipe {
@Id
private String id;
private final String recipeName;
private final String recipeDescription;
private final String difficulty;
private ArrayList<Ingredient> ingredients;
private Recipe(Builder builder) {
this.recipeName = builder.recipeName;
this.recipeDescription = builder.recipeDescription;
this.difficulty = builder.difficulty;
this.ingredients = builder.ingredients;
}
public static class Builder {
private String recipeName;
private String recipeDescription;
private String difficulty;
private ArrayList<Ingredient> ingredients;
public Builder(String recipeName, String recipeDescription, String difficulty, ArrayList<Ingredient> ingredients){
this.recipeName = recipeName;
this.recipeDescription = recipeDescription;
this.difficulty = difficulty;
this.ingredients = ingredients;
}
public Recipe build() {
return new Recipe(this);
}
}
// Getter and Setter... //
Она написана с использованием шаблона Builder.
Затем у меня есть этот контроллер, где я хочу ПУБЛИКОВАТЬ новые рецепты:
@RestController
public class RecipeController {
@Autowired
RecipesRepository repo;
@PostMapping("/postRecipe")
public ResponseEntity postNewRecipe(@RequestBody Recipe r) {
repo.save(r);
return ResponseEntity.ok(r);
}
}
Если я отправляю объект в «/ postRecipe», который выглядит точно так же, как модель «Recipe», я всегда получаю ошибку 500, хотя:
{
"timestamp": "2020-08-18T15:40:49.664 00:00",
"status": 500,
"error": "Internal Server Error",
"trace": "org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class com.petziferum.rezeptbuch.models.Recipe]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.petziferum.rezeptbuch.models.Recipe` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)n at [Source: (PushbackInputStream); line: 2, column: 3]rntat org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:242)rntat org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:227)rntat org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:205)rntat org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:158)rntat org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:131)rntat org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)rntat org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:167)rntat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:134)rntat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105)rntat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:879)rntat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793)rntat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)rntat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)rntat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)rntat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)rntat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)rntat javax.servlet.http.HttpServlet.service(HttpServlet.java:660)rntat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)rntat javax.servlet.http.HttpServlet.service(HttpServlet.java:741)rntat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)rntat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)rntat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)rntat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)rntat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)rntat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)rntat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)rntat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)rntat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)rntat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)rntat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)rntat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)rntat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)rntat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)rntat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)rntat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)rntat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)rntat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)rntat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)rntat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)rntat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)rntat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)rntat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)rntat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)rntat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373)rntat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)rntat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)rntat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590)rntat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)rntat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)rntat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)rntat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)rntat java.base/java.lang.Thread.run(Thread.java:834)rnCaused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.petziferum.rezeptbuch.models.Recipe` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)n at [Source: (PushbackInputStream); line: 2, column: 3]rntat com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)rntat com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1611)rntat com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:400)rntat com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1077)rntat com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1320)rntat com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:331)rntat com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:164)rntat com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4482)rntat com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3487)rntat org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:239)rnt... 51 morern",
"message": "Type definition error: [simple type, class com.petziferum.rezeptbuch.models.Recipe]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.petziferum.rezeptbuch.models.Recipe` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)n at [Source: (PushbackInputStream); line: 2, column: 3]",
"path": "/postRecipe"
}
Как я могу правильно сохранить объект? Возможно, Spring не может обрабатывать шаблон Builder?
Комментарии:
1. Почему вы используете builder в этом случае? Для перехода в спящий режим требуется пустой конструктор. Добавьте
public Recipe() {}
в свой класс Recipe.
Ответ №1:
Spring не использует шаблон проектирования builder для создания объекта. Но конструктор по умолчанию и get / setters являются обязательными. Добавьте отсутствующий конструктор по умолчанию без аргументов (как ясно указано в ошибке) и установщики в классе Recipe. Также добавьте аннотации @GenratedValue к атрибуту Id для генерации динамического идентификатора в отсутствие значения Id.
Ответ №2:
Дополнение к ответу, предоставленному сачином,
Я бы порекомендовал вам добавить Lombok в свой проект и добавить
@NoArgsConstructor @Getter @Setter
Аннотации к классу.
Итак, код должен выглядеть следующим образом
@NoArgsConstructor
@Getter
@Setter
Document(collection = "recipes") public class Recipe {
Что это делает?
Lombok — это библиотека, которая уменьшает объем шаблонного кода, который вам нужно написать. По сути, вы инструктируете Lombok написать пустой конструктор, все средства получения и все средства установки.
Когда происходит JPA или любая другая десериализация, создается объект с пустым конструктором, а затем присоединяются свойства. Это делается для того, чтобы инициализировать прокси, которые позже смогут обрабатывать отложенные загрузки.
Итак, если вы хотите, чтобы это сработало, одним из доступных способов является использование конструктора All args, который менее рекомендуется. Или NoArgsConstructor плюс средства получения и настройки.
Кроме того, если вы заинтересованы в использовании builder, вы можете использовать @Builder
along with @AllArgsConstructor
из Lombok. Но не для десериализации из базы данных.