在 Spring boot 中使用 post 方法保存多个实体时的无限循环

Posted

技术标签:

【中文标题】在 Spring boot 中使用 post 方法保存多个实体时的无限循环【英文标题】:Infinite loop when saving multiple entities with a post-method in Spring boot 【发布时间】:2018-01-09 17:11:14 【问题描述】:

为了解释我正在处理的问题,我将首先提供代码。

配方控制器

@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 告诉数据库已经进行了更改(将配方添加到他们的列表中)时,似乎存在添加新用户的无限循环。

【问题讨论】:

可能重复:***.com/questions/3325387/… @MehrajMalik 问题和答案似乎有点过时了。我不认为这个可以帮助我。还是谢谢。 ***.com/questions/45362300/…,所有技巧也适用于当前任务 这甚至是由保存操作引起的吗?在我看来,Jackson 正试图在您的getAllUsers() 中序列化您的EvaUser,并且由于它引用了RecipeRecipe 引用了EvaUser,它无法正确序列化它并最终进入一个无限循环映射该链。 @MehrajMalik 提供的通过添加 @JsonIgnore 来打破链条的答案实际上是一个正确的解决方案,并且并没有过时。 【参考方案1】:

回答您的问题并包括您的 cmets 的最后要求。

如果你想打破循环,但有些人想保留嵌套对象,我建议编写一个自定义序列化程序并将导致无限递归的对象替换为其他字段(我使用作者用户名,即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

来自控制器的EvaUser 的JSON 响应正文(之前是***Error):


    "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"

【讨论】:

刚刚也偶然发现了答案。我确实犯了将作者用作包含配方的对象的错误,从而导致循环。谢谢。

以上是关于在 Spring boot 中使用 post 方法保存多个实体时的无限循环的主要内容,如果未能解决你的问题,请参考以下文章

500内部服务器错误;在spring Boot rest api中使用POST方法时

从 POST 方法中使用 JDBCTemplate 存储对象数组 - Spring Boot

Spring Boot 应用 Post 方法调用,405: Method Not Allowed

Spring Boot - 不允许使用 POST 方法

如何使用 Spring Boot 在 Post 方法中传递 Json 数据?我想传递几个变量并在不同的 java 类中使用这些变量

spring boot拦截器中获取request post请求中的参数