在 Spring 引导中使用 post 方法保存多个实体时无限循环
Infinite loop when saving multiple entities with a post-method in Spring boot
为了解释我正在处理的问题,我将首先提供代码。
配方控制器
@RequestMapping(path = "/addrecipe")
public void addNewRecipe(@RequestBody AddRecipeDto addRecipeDto){
Recipe newRecipe = new Recipe();
EvaUser user = evaUserRepository.findOne(addRecipeDto.getUserId());
for(Ingredient ingredient: addRecipeDto.getIngredients()){
ingredientRepository.save(ingredient);
}
newRecipe.setTitle(addRecipeDto.getTitle());
newRecipe.setAuthor(user);
newRecipe.setDescription(addRecipeDto.getDescription());
newRecipe.setIngredients(addRecipeDto.getIngredients());
recipeRepository.save(newRecipe);
user.getMyRecipes().add(newRecipe);
evaUserRepository.save(user);
}
用户控制器
@RequestMapping("/getusers")
public Iterable<EvaUser> getAllUsers() {
return evaUserRepository.findAll();
}
EvaUser
@OneToMany
private List<Recipe> myRecipes;
@ManyToMany
private List<Recipe> favoriteRecipes;
食谱
@ManyToOne
private EvaUser author;
异常
Failed to write HTTP message:
org.springframework.http.converter.HttpMessageNotWritableException: Could
not write content: Infinite recursion
问题
所以当我调用添加菜谱的方法时,我想让数据库知道有一个新菜谱,并且新菜谱链接到添加它的用户。当我删除保存用户实体的部分时,根本不会进行映射。但是当我使用 userRepository 告诉数据库已经进行了更改(将食谱添加到他们的列表中)时,似乎存在添加新用户的无限循环。
回答您的问题并包括您评论中的最后要求。
如果你想打破循环,但有些人又想保留嵌套对象,我建议编写一个自定义序列化程序,并将导致无限递归的对象替换为其他字段(我使用作者用户名这是 String
而不是下面示例中的 Author
对象)。
为了重现案例,我创建了一个类似于您的模拟模型。
食谱:
public class Recipe {
private EvaUser author;
private String name = "test";
private String ingridients = "carrots, tomatos";
public EvaUser getAuthor() {
return author;
}
public void setAuthor(EvaUser author) {
this.author = author;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getIngridients() {
return ingridients;
}
public void setIngridients(String ingridients) {
this.ingridients = ingridients;
}
}
EvaUser:
public class EvaUser {
private List<Recipe> myRecipes = new ArrayList<>();
private List<Recipe> favoriteRecipes = new ArrayList<>();
private String username;
public List<Recipe> getMyRecipes() {
return myRecipes;
}
public void setMyRecipes(List<Recipe> myRecipes) {
this.myRecipes = myRecipes;
}
public List<Recipe> getFavoriteRecipes() {
return favoriteRecipes;
}
public void setFavoriteRecipes(List<Recipe> favoriteRecipes) {
this.favoriteRecipes = favoriteRecipes;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
正在创建自定义序列化程序:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import java.io.IOException;
import java.util.Optional;
public class RecipeSerializer extends StdSerializer<Recipe> {
protected RecipeSerializer() {
this(null);
}
protected RecipeSerializer(Class<Recipe> t) {
super(t);
}
@Override
public void serialize(Recipe recipe, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeStartObject();
gen.writeStringField("name", recipe.getName());
gen.writeStringField("author", Optional.ofNullable(recipe.getAuthor().getUsername()).orElse("null"));
gen.writeStringField("ingridients", recipe.getIngridients());
gen.writeEndObject();
}
}
正在应用序列化器:
@JsonSerialize(using = RecipeSerializer.class)
public class Recipe {
// model entity
}
JSON 来自控制器的 EvaUser
的响应主体(前一个是 WhosebugError
):
{
"myRecipes": [
{
"name": "soup",
"author": "user1",
"ingridients": "carrots, tomatos"
},
{
"name": "steak",
"author": "user1",
"ingridients": "meat, salt"
}
],
"favoriteRecipes": [
{
"name": "soup",
"author": "user1",
"ingridients": "carrots, tomatos"
},
{
"name": "steak",
"author": "user1",
"ingridients": "meat, salt"
}
],
"username": "user1"
}
为了解释我正在处理的问题,我将首先提供代码。
配方控制器
@RequestMapping(path = "/addrecipe")
public void addNewRecipe(@RequestBody AddRecipeDto addRecipeDto){
Recipe newRecipe = new Recipe();
EvaUser user = evaUserRepository.findOne(addRecipeDto.getUserId());
for(Ingredient ingredient: addRecipeDto.getIngredients()){
ingredientRepository.save(ingredient);
}
newRecipe.setTitle(addRecipeDto.getTitle());
newRecipe.setAuthor(user);
newRecipe.setDescription(addRecipeDto.getDescription());
newRecipe.setIngredients(addRecipeDto.getIngredients());
recipeRepository.save(newRecipe);
user.getMyRecipes().add(newRecipe);
evaUserRepository.save(user);
}
用户控制器
@RequestMapping("/getusers")
public Iterable<EvaUser> getAllUsers() {
return evaUserRepository.findAll();
}
EvaUser
@OneToMany
private List<Recipe> myRecipes;
@ManyToMany
private List<Recipe> favoriteRecipes;
食谱
@ManyToOne
private EvaUser author;
异常
Failed to write HTTP message:
org.springframework.http.converter.HttpMessageNotWritableException: Could
not write content: Infinite recursion
问题
所以当我调用添加菜谱的方法时,我想让数据库知道有一个新菜谱,并且新菜谱链接到添加它的用户。当我删除保存用户实体的部分时,根本不会进行映射。但是当我使用 userRepository 告诉数据库已经进行了更改(将食谱添加到他们的列表中)时,似乎存在添加新用户的无限循环。
回答您的问题并包括您评论中的最后要求。
如果你想打破循环,但有些人又想保留嵌套对象,我建议编写一个自定义序列化程序,并将导致无限递归的对象替换为其他字段(我使用作者用户名这是 String
而不是下面示例中的 Author
对象)。
为了重现案例,我创建了一个类似于您的模拟模型。
食谱:
public class Recipe {
private EvaUser author;
private String name = "test";
private String ingridients = "carrots, tomatos";
public EvaUser getAuthor() {
return author;
}
public void setAuthor(EvaUser author) {
this.author = author;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getIngridients() {
return ingridients;
}
public void setIngridients(String ingridients) {
this.ingridients = ingridients;
}
}
EvaUser:
public class EvaUser {
private List<Recipe> myRecipes = new ArrayList<>();
private List<Recipe> favoriteRecipes = new ArrayList<>();
private String username;
public List<Recipe> getMyRecipes() {
return myRecipes;
}
public void setMyRecipes(List<Recipe> myRecipes) {
this.myRecipes = myRecipes;
}
public List<Recipe> getFavoriteRecipes() {
return favoriteRecipes;
}
public void setFavoriteRecipes(List<Recipe> favoriteRecipes) {
this.favoriteRecipes = favoriteRecipes;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
正在创建自定义序列化程序:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import java.io.IOException;
import java.util.Optional;
public class RecipeSerializer extends StdSerializer<Recipe> {
protected RecipeSerializer() {
this(null);
}
protected RecipeSerializer(Class<Recipe> t) {
super(t);
}
@Override
public void serialize(Recipe recipe, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeStartObject();
gen.writeStringField("name", recipe.getName());
gen.writeStringField("author", Optional.ofNullable(recipe.getAuthor().getUsername()).orElse("null"));
gen.writeStringField("ingridients", recipe.getIngridients());
gen.writeEndObject();
}
}
正在应用序列化器:
@JsonSerialize(using = RecipeSerializer.class)
public class Recipe {
// model entity
}
JSON 来自控制器的 EvaUser
的响应主体(前一个是 WhosebugError
):
{
"myRecipes": [
{
"name": "soup",
"author": "user1",
"ingridients": "carrots, tomatos"
},
{
"name": "steak",
"author": "user1",
"ingridients": "meat, salt"
}
],
"favoriteRecipes": [
{
"name": "soup",
"author": "user1",
"ingridients": "carrots, tomatos"
},
{
"name": "steak",
"author": "user1",
"ingridients": "meat, salt"
}
],
"username": "user1"
}