Проблема Spring Data JPA с обновлением нескольких сущностей - PullRequest
0 голосов
/ 21 января 2019

У меня есть 2 класса сущностей: "Menu", в котором есть только одно поле с именем "name", и вторая сущность - "Ingredients", в которой есть 2 поля - "ingredientName" и "ingredientDescription". Структура базы данных

Я создаю простое веб-приложение CRUD, но метод обновления вместо обновления сущности вставляет новые значения в БД. Я проверил, и когда пользователь нажимает на обновление в указанном меню , у первого субъекта id и его идентификаторов ингредиентов также предопределены. Я новичок в Spring Boot и Thimeleaf и не знаю, как работать с JPA, когда у вас более 1 сущности.

Элемент меню:

@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";
}

edit-page.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 [] с помощью String[] ingNameSplit = ingName.split(","), поскольку input будет одной большой строкой, разделенной запятыми, а не array []. Затем я получаю меню, которое пользователь хочет обновить - Menu menuToUpdate = menuRepository.getOne(menu.getId()); menu.getId() не является нулевым, потому что я установил скрытые поля "id" в тимелист. Затем я получаю ингредиенты этого меню - List<Ingredients> 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);

Спасибо за помощь, ребята:)

Ответы [ 3 ]

0 голосов
/ 22 января 2019

На данный момент:

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


menuRepository.save(menu);

return "redirect:/recipeList";
}

Вы получаете меню от edit-page.html и у него нет идентификатора, поэтому он всегда создает новые записи в базе данных. Чтобы редактировать желаемое меню, вам нужно иметь его идентификатор раньше. Вы можете создать конечную точку для получения списка меню и отображать их с помощью кнопки редактирования рядом с каждым меню на html-сайте. Затем, если пользователь нажимает кнопку редактирования, перенаправьте его в форму редактирования, но на этот раз вы можете передать идентификатор меню.

0 голосов
/ 22 января 2019

Добавьте скрытое поле идентификатора для хранения идентификатора меню и добавьте скрытые поля идентификатора для каждого ингредиента.

0 голосов
/ 22 января 2019

Сначала вам нужно извлечь Menu сущность из базы данных с помощью id, используя getOne, а затем вы можете обновить ее.

Измените свой код в методе postEdit следующим образом:

Получить объект меню:

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

Обновить атрибуты:

menuToUpdate.setName(menu.getName());

Сохранить объект:

menuRepository.save(menuToUpdate); 
...