【问题标题】:Spring Data JPA problem with updating multiple Entities更新多个实体的 Spring Data JPA 问题
【发布时间】:2019-06-15 06:53:57
【问题描述】:

我有 2 个实体类 "Menu",它只有一个名为 "name" 的字段和第二个实体 - "Ingredients",它有 2 个字段 - "ingredientName""ingredientDescription"Database Structure

我正在创建一个简单的 CRUD web-app ,但更新方法不是更新 Entity ,而是在数据库中插入新值。我检查了当用户点击指定 menu 上的更新时,第一个实体的 id 及其 ingredients id's 也是预先定义的。我是 Spring Boot 和 thymeleaf 的新手,当您拥有超过 1 个实体时,我真的不知道如何使用 JPA。

菜单实体:

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private String id;

@Column(name = "name")
private String name;

// Mapping To second table
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinTable(name = "menu_ingredient",
        joinColumns = @JoinColumn(name = "menu_id"),
        inverseJoinColumns = @JoinColumn(name = "ingredient_id"))
private List<Ingredients> ingredient = new ArrayList<>();

//Getters/Setters/Constructors/ToString

成分实体:

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;

@Column(name = "ingredient")
private String ingredientName;

@Column(name = "description")
private String ingredientDescription;

//Getters/Setters/Constructors/ToString

控制器(仅更新方法):

@GetMapping("/edit/{id}")
public String edit(@PathVariable(name = "id")String id, Model model){
    Optional<Menu> menu = menuRepository.findById(id);

    List<Ingredients> ingredients = menu.get().getIngredient();

    for (Ingredients ing : ingredients){
        System.out.println(ing);
    }

    model.addAttribute("ingredients", ingredients);
    model.addAttribute("newMenu",menu);


    return "edit-page";
}

@PostMapping("/postEditMenu")
public String postEdit(@ModelAttribute("newMenu")Menu menu){



    menuRepository.save(menu);

    return "redirect:/recipeList";
}

编辑页面.html:

    <form action = "#" th:action="@{/postEditMenu}" th:object="${newMenu}" method="post">
        <p>Menu Name: <br><input type="text" th:field="*{name}"></p>
        <div id="wrapper" th:each="ing: ${ingredients}">
            <label for="ingredientName"></label>
            <p>Ingredient Name: <br><input th:value="${ing.ingredientName}" id="ingredientName" type="text" name="ingName"></p>

            <label for="ingredientDescription"></label>
            <p>Ingredient Description:</p> <textarea id="ingredientDescription" type="text" th:text="${ing.ingredientDescription}" name="ingDesc"></textarea>
        </div>
        <br>

        <input type="button" id="more_fields" onclick="add_fields();" value="Add More" />
        <br>

        <input type="submit" th:value="Submit">

    </form>

FIX我实际上是在以下答案的帮助下弄清楚的。这是代码:

@PostMapping("/postEditMenu")
public String postEdit(@ModelAttribute("newMenu")Menu menu,
                       @RequestParam String ingName,
                       @RequestParam String ingDesc){

    String[] ingNameSplit = ingName.split(",");
    String[] ingDescSplit = ingDesc.split(",");

    Menu menuToUpdate = menuRepository.getOne(menu.getId());


    List<Ingredients> newIngredientList = menuToUpdate.getIngredient();
    newIngredientList.clear();

    for(int a = 0, b = 0; a<ingNameSplit.length; b++, a++){
        newIngredientList.add(new Ingredients(ingNameSplit[a], ingDescSplit[b]));
    }
    menuToUpdate.setIngredient(newIngredientList);

    menuRepository.save(menuToUpdate);

    return "redirect:/recipeList";
}

所以,首先我为每个所需的项目添加了隐藏的“id”字段,如下所示:

<input type="text" th:field = "*{id}" hidden>

<input type="text" th:value = "${ing.id}" hidden>

然后,在postEditMenu 方法中,我添加了@RequestParam String ingName,@RequestParam String ingDesc 以从百里香获取新项目的输入,然后我拆分该字符串并将其添加到带有String[] ingNameSplit = ingName.split(",") 的String[] 数组中,因为输入将是一个大逗号分隔的 String 而不是 array[] 。然后我得到用户想要更新的菜单 - Menu menuToUpdate = menuRepository.getOne(menu.getId()); menu.getId() 不为空,因为我在百里香中设置了隐藏的“id”字段。然后我得到这个菜单的成分 - List&lt;Ingredients&gt; newIngredientList = menuToUpdate.getIngredient(); 因为列表中已经填满了现有的成分我清除了该列表并添加了用户将填写表格的新成分 -

for(int a = 0, b = 0; a<ingNameSplit.length; b++, a++){
            newIngredientList.add(new Ingredients(ingNameSplit[a], ingDescSplit[b]));
        }

之后我设置了这个newIngredientsList 并将菜单本身保存到数据库中 -

menuToUpdate.setIngredient(newIngredientList);

menuRepository.save(menuToUpdate);

感谢大家的帮助:)

【问题讨论】:

    标签: java spring-boot jpa spring-data-jpa thymeleaf


    【解决方案1】:

    添加一个隐藏的 id 字段来保存菜单的 id,并为每种成分添加隐藏的 id 字段。

    【讨论】:

      【解决方案2】:

      此时:

      @PostMapping("/postEditMenu")
      public String postEdit(@ModelAttribute("newMenu")Menu menu){
      
      
      menuRepository.save(menu);
      
      return "redirect:/recipeList";
      }
      

      您从 edit-page.html 收到菜单并且它没有 id,这就是它总是在数据库中创建新记录的原因。 要编辑所需的菜单,您需要先拥有它的 id。 您可以创建用于获取菜单列表的端点,并使用 html 站点中每个菜单旁边的编辑按钮显示它们。然后,如果用户单击编辑按钮将他重定向到您的编辑表单,但这次您可以传递菜单的 ID。

      【讨论】:

      • 我确实有我的 edit-page.html ,那么如何将 Id 传递回控制器?它还会更新成分吗?因为如果你看我的表格,我会同时更新
      • 我添加了隐藏的 id 值,但仍然只有菜单更新,成分保持不变。
      【解决方案3】:

      首先,您需要使用id 使用getOne 从数据库中获取Menu 实体,然后您可以对其进行更新。

      postEdit 方法中编辑您的代码,如下所示:

      获取菜单实体:

      Menu menuToUpdate = menuRepository.getOne(menu.getId());
      

      更新属性:

      menuToUpdate.setName(menu.getName());

      保存实体:

      menuRepository.save(menuToUpdate); 
      

      【讨论】:

      • id 将为空,因为他没有发送一个。此外,如果指定了 id,他不需要从存储库中按 id 获取菜单,因为 menuRepository.save(menu) 在这种情况下会更新实体,而不是保存新的。
      • 所以我添加了隐藏的 id 值,您的代码只更新菜单而不是成分。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-30
      • 2012-08-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-04
      相关资源
      最近更新 更多