英文:
Spring boot PostMapping to Save a Object
问题
我正在学习Spring的初级阶段。
我想要编写一个后端,用于保存烹饪食谱的烹饪应用程序。
为此,我有以下“Recipe”模型:
@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 Pattern)。
然后,我有以下控制器,我希望可以通过POST请求添加新的食谱:
@RestController
public class RecipeController {
@Autowired
RecipesRepository repo;
@PostMapping("/postRecipe")
public ResponseEntity postNewRecipe(@RequestBody Recipe r) {
repo.save(r);
return ResponseEntity.ok(r);
}
}
然而,当我将一个看起来和“Recipe”模型完全相同的对象发送到“/postRecipe”时,我总是得到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)..."
}
你如何能够正确保存这个对象?也许Spring无法处理建造者模式?
英文:
Im at the Beginning of learning Spring.
I want to write a Backend which saves Recipes for a cookbook App.
Therefore have This Model "Recipe":
@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... //
Its written with a Builder Pattern.
Then i have This Controller, where i want to POST new Recipes:
@RestController
public class RecipeController {
@Autowired
RecipesRepository repo;
@PostMapping("/postRecipe")
public ResponseEntity postNewRecipe(@RequestBody Recipe r) {
repo.save(r);
return ResponseEntity.ok(r);
}
}
If i send a Object to "/postRecipe" which looks exactly like the "Recipe"-Model im always getting a Error 500 though:
{
"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]\r\n\tat org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:242)\r\n\tat org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:227)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:205)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:158)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:131)\r\n\tat org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:167)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:134)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:879)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793)\r\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)\r\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)\r\n\tat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:660)\r\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:741)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)\r\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)\r\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)\r\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)\r\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\r\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)\r\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)\r\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373)\r\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)\r\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)\r\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590)\r\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\r\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\r\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\r\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\r\n\tat java.base/java.lang.Thread.run(Thread.java:834)\r\nCaused 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]\r\n\tat com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)\r\n\tat com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1611)\r\n\tat com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:400)\r\n\tat com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1077)\r\n\tat com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1320)\r\n\tat com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:331)\r\n\tat com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:164)\r\n\tat com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4482)\r\n\tat com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3487)\r\n\tat org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:239)\r\n\t... 51 more\r\n",
"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"
}
How am i able to save the Object correctly? Maybe Spring cant handle the Builder Pattern?
答案1
得分: 2
春季不使用构建器设计模式来创建对象。但是默认构造函数和get/set方法是必需的。
在Recipe类中添加缺失的无参默认构造函数(如错误明确所示)和set方法。
还要在Id属性上添加@GenratedValue注解,以在缺少Id值时生成动态Id。
英文:
Spring don't use builder design pattern to create object. But a default constructor and get/setters are mandatory.
Add the missing no-argument default constructor (as the error clearly says) and the setters in Recipe class.
Also add @GenratedValue annotations on the Id attribute to generate dynamic Id in absence of Id value.
答案2
得分: 0
除了sachin提供的答案之外,
我建议您将Lombok添加到您的项目中,并且添加
@NoArgsConstructor
@Getter
@Setter
这些注解到类中。
所以代码应该看起来像这样
@NoArgsConstructor
@Getter
@Setter
Document(collection = "recipes") public class Recipe {
这样做有什么作用?
Lombok是一个可以减少您需要编写的样板代码量的库。实质上,您正在指示Lombok编写一个空构造函数,以及所有的getter和setter。
当发生JPA或任何反序列化时,它会创建一个带有空构造函数的对象,然后附加属性。这是为了初始化稍后可以处理延迟加载的代理。
因此,如果您希望它正常工作,可用的一种方法是使用全参数构造函数,但这不是最推荐的方法。或者使用NoArgsConstructor加上Getter和Setter。
另外,如果您想使用builder,您可以在Lombok中使用@Builder
,以及@AllArgsConstructor
。但不适用于从数据库进行反序列化。
英文:
Addition to the answer provided by sachin,
I would recommend you to add Lombok to your project and add
@NoArgsConstructor
@Getter
@Setter
Annotations to the class.
So the code should look like
@NoArgsConstructor
@Getter
@Setter
Document(collection = "recipes") public class Recipe {
What does this do?
Lombok is a library that reduces the amount of boilerplate code you need to write. Essentially you are instructing Lombok to write an empty constructor, all getters and all setter.
When JPA or any deserialisation happens, it creates an object with empty constructor, and then attach properties. This is done in order to initialize proxies that can handle lazy loads later on.
So if you want it to work, one available ways are using All args constructor, which is less recommended. Or NoArgsConstructor plus Getter and Setters.
Also, if you are keen on using builder, you can use @Builder
along with @AllArgsConstructor
from Lombok. But not for deserialization from database.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论