I am in the process of adding a DTO layer to a restful api. Before, the program used entity (Recipe and Ingredient) directly and now I added a new DTO layer in between (RecipeDTO IngredientDTO). However, the moment I made the change I started getting Null values from @RequestBody. Each recipe contains a list of Ingredients and it is the list of ingredients that are returning null values, the recipe by itself is returning fine.
The controller looks like this
package com.example.recipes.controller;
import com.example.recipes.DTO.RecipeDTO;
import com.example.recipes.Service.RecipeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/*...*/
@PostMapping(path = "/post")
public void postRecipes(@RequestBody RecipeDTO recipeDTO){
recipeService.postRecipes(recipeDTO);
}
/*...*/
Recipe Entity
package com.example.recipes.Entity;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.List;
@Data
@Entity
@Table(name = "recipe", schema = "public")
@AllArgsConstructor
@NoArgsConstructor
public class Recipe {
@Id
@GeneratedValue(
strategy = GenerationType.IDENTITY
)
@Column(name = "id", updatable = false, nullable = false)
private long id;
@Column(name = "name")
private String name;
@Column(name = "instructions")
private String instructions;
@OneToMany(mappedBy = "recipe")
private List<Ingredient> ingredients;
@JsonProperty("date_added")
private String dateAdded = String.valueOf(LocalDateTime.now());
@JsonProperty("last_edited")
private String lastEdited = String.valueOf(LocalDateTime.now());
}
RecipeDTO
package com.example.recipes.DTO;
import lombok.*;
import javax.persistence.OneToMany;
import java.time.LocalDateTime;
import java.util.List;
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@ToString
public class RecipeDTO {
private long id;
private String name;
private String instructions;
private List<IngredientDTO> ingredientsDTO;
private String dateAdded = String.valueOf(LocalDateTime.now());
private String lastEdited = String.valueOf(LocalDateTime.now());
public RecipeDTO(long id, String name, String instructions, String dateAdded, String lastEdited) {
this.id = id;
this.name = name;
this.instructions = instructions;
this.dateAdded = dateAdded;
this.lastEdited = lastEdited;
}
}
Ingredient Entity
package com.example.recipes.Entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import javax.persistence.*;
@Data
@Entity
@Table(name = "Ingredient")
@NoArgsConstructor
public class Ingredient {
@Id
@GeneratedValue(
strategy = GenerationType.IDENTITY
)
@JsonProperty("ingredient_id")
private long ingredient_ID;
@JsonProperty("ingredient_name")
private String ingredientName;
@Column(name = "amount")
private int amount;
@Column(name = "unit")
private String unit;
@ManyToOne
@JoinColumn(name = "recipe_id")
@ToString.Exclude
@JsonIgnore
private Recipe recipe;
}
IngredientDTO
package com.example.recipes.DTO;
import lombok.*;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class IngredientDTO {
private long ingredientID;
private String ingredientName;
private int amount;
private String unit;
}
the json i sent
{
"name":"unique2",
"ingredients":[
{
"ingredient_name":"Atlantic",
"amount":13,
"unit":"ton"
},
{
"ingredient_name":"Pacific",
"amount":15,
"unit":"boatload"
},
{
"ingredient_name":"Indian",
"amount":38,
"unit":"trucload"
}
],
"instructions":"easy on the salt"
}
and the @requestbody the ingredientsDTO is null
this is recipe: RecipeDTO(id=0, name=unique2, instructions=easy on the salt, ingredientsDTO=null, dateAdded=2022-08-08T15:04:10.678748100, lastEdited=2022-08-08T15:04:10.678748100)
Edit: I have just tried copying the code from the entity classes and pasting them in the DTO classes and it still returning null...
package com.example.recipes.DTO;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.List;
@Data
@Entity
@Table(name = "recipe", schema = "public")
@AllArgsConstructor
@NoArgsConstructor
public class RecipeDTO {
@Id
@GeneratedValue(
strategy = GenerationType.IDENTITY
)
@Column(name = "id", updatable = false, nullable = false)
private long id;
@Column(name = "name")
private String name;
@Column(name = "instructions")
private String instructions;
@OneToMany(mappedBy = "recipeDTO")
private List<IngredientDTO> ingredientDTOs;
@JsonProperty("date_added")
private String dateAdded = String.valueOf(LocalDateTime.now());
@JsonProperty("last_edited")
private String lastEdited = String.valueOf(LocalDateTime.now());
public RecipeDTO(long id, String name, String instructions, String dateAdded, String lastEdited) {
this.id = id;
this.name = name;
this.instructions = instructions;
this.dateAdded = dateAdded;
this.lastEdited = lastEdited;
}
}
package com.example.recipes.DTO;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import javax.persistence.*;
@Data
@Entity
@Table(name = "Ingredient")
@NoArgsConstructor
public class IngredientDTO {
@Id
@GeneratedValue(
strategy = GenerationType.IDENTITY
)
@JsonProperty("ingredient_id")
private long ingredientID;
@JsonProperty("ingredient_name")
private String ingredientName;
@Column(name = "amount")
private int amount;
@Column(name = "unit")
private String unit;
@ManyToOne
@JoinColumn(name = "recipe_id")
@ToString.Exclude
@JsonIgnore
private RecipeDTO recipeDTO;
public IngredientDTO(long ingredientID, String ingredientName, int amount, String unit) {
this.ingredientID = ingredientID;
this.ingredientName = ingredientName;
this.amount = amount;
this.unit = unit;
}
}
@RequestBody
this is recipe: RecipeDTO(id=0, name=unique2, instructions=easy on the salt, ingredientDTOs=null, dateAdded=2022-08-08T15:24:19.325806500, lastEdited=2022-08-08T15:24:19.325806500)
these are the ingredients: null
this is ingredientDTO: null
this is ingredientDTO: null
Edit2: I tried posting only the ingredientDTO and the @RequestBody was able to pick it up just fine
//this is fine
public void testRecipePost(@RequestBody IngredientDTO ingredientDTO) {
System.out.println("ingredientDTO: " ingredientDTO);
}
CodePudding user response:
You can replace
@OneToMany(mappedBy = "recipeDTO")
private List<IngredientDTO> ingredientDTOs;
to
@OneToMany(mappedBy = "recipeDTO")
private List<IngredientDTO> ingredients;
Or adding
@JsonProperty("ingredients")
Example:
@JsonProperty("ingredients")
@OneToMany(mappedBy = "recipeDTO")
private List<IngredientDTO> ingredientDTOs;
The reason for null
is because Jackson doesn't know how to deserialise your fields properly with different names.
CodePudding user response:
In the json, the name is ingredients
but, in the DTO, it is ingredientsDTO
. Those 2 need to match.
CodePudding user response:
You request
{
"name":"unique2",
"ingredients":[...]
here the name of array you are passing in Json is different in the entity you are using.
Your DTO
@OneToMany(mappedBy = "recipeDTO")
private List<IngredientDTO> ingredientDTOs;
The name of fields in JSON request and Entity must match.
Change the name of field private List<IngredientDTO> ingredientDTOs;
to ingredients
.