Id превращается в null после того, как достаю объект из модели
Есть 2 контроллера:
@GetMapping("/productUpdate")
public String productUpdateGet(@RequestParam Long productId,
Model model){
Product productForUpdate = productService.findProductById(productId);
System.out.println(productForUpdate.getId());
System.out.println(productForUpdate.getProductStatus());
model.addAttribute("productForUpdate", productForUpdate);
return "productTemplates/productUpdate";
}
@PostMapping("/productUpdate")
public String productUpdatePost(@ModelAttribute("productForUpdate") Product productForUpdate,
@RequestParam ProductStatus productStatus,
@RequestParam Long productID){
System.out.println(productForUpdate.getId());
System.out.println(productForUpdate.getProductStatus());
return "redirect:/allProducts";
}
Первый контроллер выводит в консоль и id и productStatus, а второй вместо id выводит null, productStatus остаётся прежним. То есть по какой-то причине, после того, как я достаю объект из @ModelAttribute его id сбрасывается в null. Пожалуйста, объясните почему это происходит и как это исправить.
Вот модель объекта
package com.example.mywms.Model;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import javax.validation.constraints.*;
import java.util.HashSet;
import java.util.Set;
@Entity
@Table(name = "product")
@Getter
@Setter
public class Product extends BaseEntity{
@NotBlank(message = "Поле не должно быть пустым")
private String productName;
@Enumerated(EnumType.STRING)
private ProductType productType;
@NotBlank(message = "Поле не должно быть пустым")
@Size(min = 10, max = 255, message = "Описание товара должно быть от 10 до 255 символов")
private String productDescription;
@Positive(message = "Цена товара не может быть меньше 0 или быть равной 0")
private double productPrice;
@PositiveOrZero(message = "Количесвто товара не может быть меньше 0")
private int productStock;
@Enumerated(EnumType.STRING)
private ProductStatus productStatus;
@ManyToMany
@JoinTable(
name = "product_delivery",
joinColumns = {
@JoinColumn(name = "product_id"),
},
inverseJoinColumns = @JoinColumn(name = "delivery_id")
)
private Set<Delivery> deliveriesProductIn = new HashSet<>();
}
Вот HTML код
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Изменить</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"/>
</head>
<body>
<div class="container">
<div class="page-header">
<h1>
Warehouse Management System
<small>WMS</small>
</h1>
<nav class="navbar navbar-default">
<div class="container-fluid">
<ul class="nav navbar-nav">
<li>
<a th:href="@{/}">Главная</a>
</li>
<li>
<a th:href="@{/allProducts}">Просмотр товаров</a>
</li>
<li>
<a th:href="@{/productsManage}">Менеджмент товаров</a>
</li>
</ul>
</div>
</nav>
</div>
<p>Тестовый проект с использование Java Spring Framework и шаблонизатора Thymeleaf</p>
</div>
<div class="container">
<form th:method="POST" th:action="@{/productUpdate}" th:object="${productForUpdate}">
<div class="form-row" >
<label for="inputProductName">Название товара</label>
<input type="text" th:field="*{productName}" class="form-control" id="inputProductName" placeholder="Название товара">
<div class="valid-feedback" th:if="${#fields.hasErrors('productName')}" th:errors="*{productName}"></div>
<!--<div class="valid-feedback" th:text="${productNameErrors}"></div>-->
</div>
<div class="form-row">
<label for="inputProductDescription">Описание товара</label>
<input type="text" th:field="*{productDescription}" class="form-control" id="inputProductDescription" placeholder="Описание товара">
<div class="valid-feedback" th:if="${#fields.hasErrors('productDescription')}" th:errors="*{productDescription}"></div>
</div>
<div class="form-row">
<label for="inputProductPrice">Цена товара</label>
<input type="text" th:field="*{productPrice}" class="form-control" id="inputProductPrice" placeholder="Цена товара">
<div class="valid-feedback" th:if="${#fields.hasErrors('productPrice')}" th:errors="*{productPrice}"></div>
</div>
<div class="form-row">
<label for="inputProductStock">Количество товара</label>
<input type="text" th:field="*{productStock}" class="form-control" id="inputProductStock" placeholder="Количество товара">
<div class="valid-feedback" th:if="${#fields.hasErrors('productStock')}" th:errors="*{productStock}"></div>
</div>
<div class="form-row">
<label for="productType">Тип товара</label>
<select id="productType" th:field="*{productType}" class="form-control" name="productType">
<option th:value="BEATING" selected>BEATING</option>
<option th:value="CLOTHES">CLOTHES</option>
<option th:value="JEWELRY">JEWELRY</option>
<option th:value="FOOD">FOOD</option>
</select>
</div>
<button type="submit" class="btn btn-primary">Обновить</button>
</form>
</div>
</body>
</html>
Ответы (1 шт):
Вы, видимо, неверно представляете себе логику работы аннотации @ModelAttribute и вообще Thymeleaf.
Связи между аннотацией @ModelAttribute и Вашим методом, который делает model.addAttribute("productForUpdate", productForUpdate), нет.
Вы добавляете атрибут в объект Model для того, чтобы Thymeleaf его увидел и на этом конец. Затем, Ваш объект productForUpdate "хранится" на странице и вы можете обращаться ко всем его полям, в том числе и к ID.
Потом вы нажимете кнопку "Обновить" И вы отправляете на сервер только те поля, которые на этой HTML-странице использовали и ничего более. Так как вы не используете там ID, то и не будете его отправлять. Если хотите, чтобы Ваш ID отображался в контроллере то Вам тогда нужно и "хранить" его на странице (я в HTML не силён, но логику вы поняли — нужно его хранить НО скрыть)
<div class="container">
<form th:action="@{/productUpdate}" th:method="POST" th:object="${productForUpdate}">
<!-- HIDDEN ID/-->
<label for="inputProductId" th:type="hidden"></label>
<input type="text" th:field="*{id}" class="form-control" th:type="hidden" id="inputProductId"/>
<div class="form-row" >
<label for="inputProductName">Название товара</label>
<input type="text" th:field="*{productName}" class="form-control" id="inputProductName" placeholder="Название товара">
<div class="valid-feedback" th:if="${#fields.hasErrors('productName')}" th:errors="*{productName}"></div>
<!--<div class="valid-feedback" th:text="${productNameErrors}"></div>-->
</div>
...
P.S. Я пишу "хранится" в кавычках потому что на самом деле никакие поля объекта на странице не хранятся, естественно. Если кратко, то логика работы Thymeleaf такая ->
- Вы создаете HTML-страницу в которой используете шаблонизатор
- Затем в контроллере добавляете в специальный объект
Modelобъекты для заполнения этого шаблона. - Потом возвращаете в контроллере имя шаблона
- Затем Spring всё это дело перехватывает и парсит вашу HTML-страницу вставляя в шаблонные места ваши данные из объекта
Model - Возвращает уже ГОТОВУЮ СТРАНИЦУ.
Как вы уже поняли, на вашей страницу уже нет никаких объектов и ничего там больше не хранится. Ваша страница была обработана ещё на сервере. Поэтому если вы не используете на своей странице ID, то и взяться ему там НЕ-ОТ-КУ-ДА
P.S.S. Если хотите без таких "костылей", то используйте JavaScript и вручную всё это заполняйте. Там всё будет хранится :)