I'm stuck with such problem: How can I pass an object from the web form to the Spring controller? (I'm also using Hibernate and Thymeleaf)
I made everything like it is done here: POSTing data with many to one relationship using Thymeleaf but it's not working for me. I'm getting such error:
Field error in object 'car' on field 'engine': rejected value [1]; codes [typeMismatch.car.engine,typeMismatch.engine,typeMismatch.ua.klieshchunov.spring.memorial.model.entity.Engine,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [car.engine,engine]; arguments []; default message [engine]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'ua.klieshchunov.spring.memorial.model.entity.Engine' for property 'engine'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'ua.klieshchunov.spring.memorial.model.entity.Engine' for property 'engine': no matching editors or conversion strategy found]
I understand what does this error mean. It is trying to put the value of selected option into the field of Car object with type Engine. But I do not understand, how to make it work :P
Here is all the code which can be related to this problem
Car
@Entity
@Table(name="cars")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Car {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="car_id")
private int id;
@Column(name="car_model")
@Size(min=1, max=50, message="Model should be between 1 and 50 characters")
private String model;
@Column(name="car_price")
@Min(value=0, message="The value must me positive")
private int price;
@ManyToOne(optional = false, cascade = CascadeType.ALL)
@JoinColumn(name="engine_id")
private Engine engine;
}
Engine
@Entity
@Table(name="engines")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Engine {
@Id
@Column(name="engine_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column(name="model")
@Size(min=1, max=50, message="Model should be between 1 and 50 characters")
private String model;
}
CarController (Controller)
@GetMapping("/new")
public String newCar(@ModelAttribute("car") Car car,
Model model) {
model.addAttribute("engines", engineDAO.retrieve());
return "/car/new";
}
@PostMapping("/save")
public String create(@Valid @ModelAttribute("car") Car car,
BindingResult bindingResult,
Model model) {
if (bindingResult.hasErrors()) {
for (ObjectError objectError : bindingResult.getAllErrors()) {
System.out.println(objectError);
}
model.addAttribute("engines", engineDAO.retrieve());
return "/car/new";
}
carDAO.create(car);
return "/car/index";
}
new.html (View)
...
<form th:method="POST" th:action="@{/car/save}" th:object="${car}">
...
<label for="engine">Choose engine: </label>
<select th:field="*{engine}" id="engine">
<option th:each="dropDownItem : ${engines}"
th:value="${dropDownItem.id}"
th:text="${dropDownItem.model}" >
</option>
</select>
<br/>
<input type="submit" value="Create">
</form>
...
CodePudding user response:
All I needed to do is to change th:field value of the select block from "*{engine}" to "*{engine.id}" in form.
new.html
...
<select th:field="*{engine.id}" id="engine">
<option th:each="dropDownItem : ${engines}"
th:value="${dropDownItem.id}"
th:text="${dropDownItem.model}" >
</option>
</select>
...
And after that in controller I find the desired record in the db (engine with chosen id) and set filled Engine object to my Сar object, all other entries of which I already got from the form
CarController
@PostMapping("/save")
public String create(@Valid @ModelAttribute("car") Car car,
BindingResult bindingResult,
Model model) {
if (bindingResult.hasErrors()) {
for (ObjectError objectError : bindingResult.getAllErrors()) {
System.out.println(objectError);
}
model.addAttribute("engines", engineDAO.retrieve());
return "/car/new";
}
car.setEngine(engineDAO.read(car.getEngine().getId()));
carDAO.create(car);
return "redirect:/car";
}
Ye, coding at night wasn't really a superb idea xD