0

I'm having some issues with what I believe is my relational mapping in Hibernate. This is for an recipe keeping application.

Originally, the recipe class had a String ingredients field where all of the ingredients were stored. We realized that this didn't make sense for storing a list of ingredients in a recipe, so I'm in the process of refactoring it to be a list of a new type I created, Ingredient: List<Ingredient> ingredients. The ingredient form field is dynamically created by JS on the front end, and when submitted on a POST request, is transformed into an ArrayList of new Ingredients and then added into the Recipe model.

Whenever it reaches the line where it saves it into the database, I get this error:

Field 'ingredients' doesn't have a default value, which tells me that the field is null. However, when I use the debug tools, it shows me that newRecipe.ingredients is not null, its actually an ArrayList created from the data on the front end.

The Ingredient class looks like such:

@Entity
public class Ingredient extends AbstractEntity{

    @ManyToOne(targetEntity = Recipe.class,
            fetch = FetchType.LAZY,
            cascade = {CascadeType.MERGE, CascadeType.REMOVE})
    @NotNull(message = "Please include ingredients")
    private Recipe recipe;

    @Id
    @GeneratedValue
    private int id;

    private String ingredient;

    public Ingredient(String ingredient) {
        this.ingredient = ingredient;
    }

    public String getIngredient() {
        return ingredient;
    }

    public void setIngredient(String ingredient) {
        this.ingredient = ingredient;
    }

}

The Recipe Class is here:

@Entity
public class Recipe extends AbstractEntity {

   private String name;

   @OneToMany(mappedBy = "recipe")
   @NotNull(message = "Ingredients required")
   private List<Ingredient> ingredients = new ArrayList<Ingredient>();

   private String directions;

   @OneToMany(mappedBy = "recipe")
   private List<Instruction> instructions = new ArrayList<Instruction>();

   @NotNull(message = "Category required")
   private Category category;

   private Tag tag;

   private String img;

   @OneToMany(mappedBy = "recipe", cascade = {CascadeType.MERGE, CascadeType.REMOVE})
   @NotNull(message = "User is required")
   private List<UserRecipe> users = new ArrayList<>();

   public Recipe() {
   }

   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }


   public Category getCategory() {
      return category;
   }

   public void setCategory(Category category) {
      this.category = category;
   }

   public List<UserRecipe> getUsers() {
      return users;
   }

   public void setUsers(List<UserRecipe> users) {
      this.users = users;
   }

   public String getImg() {
      return img;
   }

   public void setImg(String img) {
      this.img = img;
   }


   public List<Ingredient> getIngredients() {
      return ingredients;
   }

   public void setIngredients(List<Ingredient> ingredients) {
      this.ingredients = ingredients;
   }

   public String getDirections() {
      return directions;
   }

   public void setDirections(String directions) {
      this.directions = directions;
   }

   public Tag getTag() {
      return tag;
   }

   public void setTag(Tag tag) {
      this.tag = tag;
   }

   public List<Instruction> getInstructions() {
      return instructions;
   }

   public void setInstructions(List<Instruction> instructions) {
      this.instructions = instructions;
   }
}

RecipeController is here:

@PostMapping("create")
   public String createRecipe(HttpServletRequest request, @ModelAttribute Recipe newRecipe,
                              @ModelAttribute @Valid String newCategory,
                              Errors errors, Model model, RedirectAttributes redirectAttrs) {

      if (errors.hasErrors()) {
         model.addAttribute("title", "Create Recipe");
         return "recipes/create";
      }

      String[] ingredients = request.getParameterValues("ingredient");

      List<Ingredient> ingredientsList = new ArrayList<Ingredient>();

      for (int i = 0; i < ingredients.length; i++) {
         Ingredient newIngredient = new Ingredient(ingredients[i]);
         ingredientsList.add(newIngredient);
      }
      newRecipe.setIngredients(ingredientsList);
      
      // THIS PRINTS AN ARRAY OF THE NUMBER OF INGREDIENTS ADDED
      System.out.println(ingredientsList.toString());

      // HERE IS WHERE MY ERROR HAPPENS
      Recipe recipe = recipeRepository.save(newRecipe);
      redirectAttrs.addAttribute("recipeId", recipe.getId());

      return "redirect:/recipes/display";
   }

My thought here is that I'm somehow not mapping the ingredients list correctly to the recipe, but I can't figure this one out and after 3 days of googling and troubleshooting here I am. Any help would be greatly appreciated. Thanks in advance!

2 Answers 2

1

This can be a problem of the schema in your database. Did you create your schema manually or are you using auto-ddl? If you created it manually, maybe you are missing a recipe_id column in the Ingredients table. If such join column has a different name, you must override it using @JoinColumn on the Ingredient class like this:

@ManyToOne(targetEntity = Recipe.class,
            fetch = FetchType.LAZY,
            cascade = {CascadeType.MERGE, CascadeType.REMOVE})
    @NotNull(message = "Please include ingredients")
    @JoinColumn("the_recipe_id") // **** Here you put the join column name you specified **** //
    private Recipe recipe;

EDIT: Also, can you post your AbstractEntity class? The problem could also be related with the lack of key in the Recipe class.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, Santiago. It was definitely an issue with the DB - i had unfortunately not changed the data type of ingredient. Creating a new DB solved this issue.
Glad to hear that! Cheers!
0

I'm an idiot. The data type was set incorrectly in the database. Creating a new DB solved it.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.