I can not update my entity using Hibernate, Spring MVC, Thymleaf - spring

Hello I want to update User entity, so in the form edit.html i'm using thymeleaf:
<form action="#" th:action="#{/{id}/edit(id=${user.id})}"
th:object="${user}" method="post">
<div class="md-form">
<input type="text" th:field="*{firstName}"
th:value="${user.firstName}" name="firstName" id="firstName"
class="form-control" /> <label class="active" for="firstName">Prénom</label>
</div>
<div class="md-form">
<input type="text" th:field="*{lastName}"
th:value="${user.lastName}" name="lastName" id="lastName"
class="form-control" /> <label class="active" for="lastName">Nom</label>
</div>
<div class="md-form">
<input type="text" th:field="*{email}" th:value="${user.email}"
name="email" id="email" class="form-control" /> <label
class="active" for="email">email</label>
</div>
<div class="md-form">
<input type="text" th:field="*{password}"
th:value="${user.password}" name="password" id="password"
class="form-control" /> <label class="active" for="password">mot
de passe</label>
</div>
<div class="md-form">
<input type="text" th:field="*{occupation}"
th:value="${user.occupation}" name="occupation" id="occupation"
class="form-control" /> <label class="active" for="occupation">profession</label>
</div>
<div class="md-form">
<input type="text" th:field="*{ville}" th:value="${user.ville}"
name="ville" id="ville" class="form-control" /> <label
class="active" for="ville">ville</label>
</div>
<div>
<input class="btn btn-sm btn-primary waves-effect btn-rounded"
type="submit" value="modifier" />
</div>
</form>
User.java:
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue
private Long id;
private String firstName;
private String lastName;
private String userName;
private int age;
private String ville;
private String email;
private String password;
private String phone;
private String company;
private String occupation;
private String img_profil;
#ManyToMany(mappedBy = "users", cascade = { CascadeType.ALL })
private Set<Discussion> discussion;
UserController.java
#GetMapping("/{userName}/edit")
public String editUser(#PathVariable String userName, Model model) {
User user = ur.findByUserName(userName).get(0);
model.addAttribute(user);
return "edit";
}
#PostMapping("/{id}/edit")
public String updateUser(#ModelAttribute("user") User user, #PathVariable("id") Long id) {
user = ur.findOne(id);
ur.save(user);
return "redirect:/find/" + user.getUserName();
}
UserRepository.java
#Repository
public interface UserRepository extends CrudRepository<User, Long> {
List<User> findByUserName(String userName);
}
PROBLEM is in the console I do not see update request (generated by Hibernate), nothing is changed on the database.

Make the following change in UserController.java:
#PostMapping("/{id}/edit")
public String updateUser(
#ModelAttribute("user") User user,
#PathVariable("id") Long id)
{
//user = ur.findOne(id);
user.setId(id);
ur.save(user);
return "redirect:/find/" + user.getUserName();
}

Related

How to iterate and save 2 objects with thymeleaf?

I dont know how fix this error, i saw some articles about that but nothing works.
Error
Resolved [org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errorsField error in object 'pupil' on field 'curse': rejected value [ModelCurse(id=2, name=PHP, division=2da, pupilList=[], teacherList=[])];
[Failed to convert property value of type 'java.lang.String' to required type 'com.example.administrator.administrator.model.ModelCurse' for property 'curse';
HTML
<form th:action="#{/pupilController/add}" th:object="${pupil}" method="post">
<div class="form-row">
<div class="form-group col-md-6">
<label>Name</label>
<input type="text" class="form-control" th:field="*{name}" placeholder="Name">
</div>
<div class="form-group col-md-6">
<label>Last Name</label>
<input type="text" class="form-control" th:field="*{lastName}" placeholder="Last Name">
</div>
<div class="form-group col-md-6">
<label>Age</label>
<input type="number" class="form-control" th:field="*{age}" placeholder="Age">
</div>
<div class="form-group col-md-6">
<label>Phone Number</label>
<input type="text" class="form-control" th:field="*{phoneNumber}" placeholder="Phone Number">
</div>
<div class="form-group col-md-6">
<select th:field="*{curse}" class="form-control">
<option th:each="curso : ${curses}"
th:value="${curso}"
th:text="${curso.name}"></option>
</select>
</div>
</div>
<input type="submit" name="btnInsert" class="btn btn-primary" value=Añadir>
</form>
Controller
#GetMapping("/addPupil")
public ModelAndView login(Model model){
ModelAndView mav = new ModelAndView("addpupil");
List<ModelCurse> modelCurses = curseService.getAllCurses();
mav.addObject("pupil",new ModelPupil());
mav.addObject("curses",modelCurses);
return mav;
}
#PostMapping("/add")
public RedirectView addPupil(#ModelAttribute("pupil")ModelPupil modelPupil){
pupilService.addPupil(modelPupil);
return new RedirectView("/pupilController/pupilList");
}
DTO
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#ToString
public class ModelCurse {
private long id;
private String name;
private String division;
private List<ModelPupil> pupilList;
private List<ModelTeacher> teacherList;
}
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#ToString
public class ModelPupil {
private long id;
private String name;
private String lastName;
private int age;
private int phoneNumber;
private ModelCurse curse;
}
When we submit the form, the value of selected option is sent as Text or String in HTTP POST. So, if you are expecting that Spring would automatically convert that String value of curse to object of type ModelCurse, then your assumption is wrong. But Spring does provide us a way to do it using Java beans PropertyEditorSupport. Here you can extend this class, and provide your own implementation on how to convert that String to the required object.

Neither BindingResult nor plain target object for bean name 'reservationBinding' available as request attribute

I started to work with thymeleaf in a basic project. When I try to put th:field="*{name}" in form, comes error with thymeleaf parsing. Only with th:object="${reservationBinding}" without any th:fields works correctly. I can't understand why doesn't work with th:filed.
Here is my controller where I set the message:
#Controller
public class ReservationController {
#GetMapping("/reservation")
public String checkFOrReservationGet(Model model){
model.addAttribute ("reservationBinding",new ReservationBinding ());
return "index";
}
#PostMapping("/reservation")
public String checkForReservation(#Valid #ModelAttribute() ReservationBinding reservationBinding,
Model model,
BindingResult bindingResult,
RedirectAttributes redirectAttributes,
HttpSession httpSession){
// model.addAttribute ("reservationBinding",new ReservationBinding ());
if(bindingResult.hasErrors ()){
redirectAttributes.addFlashAttribute ("reservationBinding",reservationBinding);
redirectAttributes.addFlashAttribute ("org.springframework.validation.BindingResult.reservationBinding",bindingResult);
return "redirect:#book-a-table";
}
return "redirect:#book-a-table";
}
Here is my html form
<form action="#book-a-table" th:action="#{/reservation}" method="post" th:object="${reservationBinding}" >
<div class="row">
<div class="col-lg-4 col-md-6 form-group" >
<input type="text" name="name" class="form-control" id="name"
placeholder="name" data-rule="minlen:4"
th:field="*{name}"
data-msg="Please enter at least 4 chars">
<div class="validate"></div>
</div>
<div class="col-lg-4 col-md-6 form-group mt-3 mt-md-0">
<input type="email" class="form-control" name="email" id="email" placeholder="Your Email" data-rule="email"
data-msg="Please enter a valid email">
<div class="validate"></div>
</div>
<div class="col-lg-4 col-md-6 form-group mt-3 mt-md-0">
<input type="text" class="form-control" name="phone" id="phone" placeholder="Your Phone" data-rule="minlen:4"
data-msg="Please enter at least 4 chars">
<div class="validate"></div>
</div>
<div class="col-lg-4 col-md-6 form-group mt-3">
<input type="text" name="date" class="form-control" id="date" placeholder="Date" data-rule="minlen:4"
data-msg="Please enter at least 4 chars">
<div class="validate"></div>
</div>
<div class="col-lg-4 col-md-6 form-group mt-3">
<input type="text" class="form-control" name="time" id="time" placeholder="Time" data-rule="minlen:4"
data-msg="Please enter at least 4 chars">
<div class="validate"></div>
</div>
<div class="col-lg-4 col-md-6 form-group mt-3">
<input type="number" class="form-control" name="people" id="people" placeholder="# of people"
data-rule="minlen:1" data-msg="Please enter at least 1 chars">
<div class="validate"></div>
</div>
</div>
<div class="form-group mt-3">
<textarea class="form-control" name="message" rows="5"
placeholder="Message"></textarea>
<div class="validate"></div>
</div>
<div class="mb-3">
<div class="loading">Loading</div>
<div class="error-message"></div>
<div class="sent-message">Your booking request was sent. We will call back or send an Email to confirm your reservation. Thank you!</div>
</div>
<div class="text-center"><button type="submit" >Send Message</button></div>
</form>
This is my bindingModel
public class ReservationBinding {
private String name;
private String email;
private String phone;
private String date;
private String time;
private int People;
private String message;
public ReservationBinding(){
}
// #Length(min = 2, message = "Username length must be minimum two characters!.")
public String getName(){
return name;
}
public ReservationBinding setName(String name){
this.name = name;
return this;
}
#Column(name = "email", nullable = false)
#Email(message = "Enter valid email address.")
public String getEmail(){
return email;
}
public ReservationBinding setEmail(String email){
this.email = email;
return this;
}
#Column(name = "phone", nullable = false)
#Length(min = 9, message = "Enter valid phone number.")
public String getPhone(){
return phone;
}
public ReservationBinding setPhone(String phone){
this.phone = phone;
return this;
}
#Column(name = "date", nullable = false)
public String getDate(){
return date;
}
public ReservationBinding setDate(String date){
this.date = date;
return this;
}
#Column(name = "time")
#Length(min = 2, message = "Enter a valid time for check in.")
public String getTime(){
return time;
}
public ReservationBinding setTime(String time){
this.time = time;
return this;
}
#Min(value = 2, message = "What is the number of the guests")
public int getPeople(){
return People;
}
public ReservationBinding setPeople(int people){
People = people;
return this;
}
public String getMessage(){
return message;
}
public ReservationBinding setMessage(String message){
this.message = message;
return this;
}
}
Do you have any idea, why alone th:object map the properties and when I put th:field doesn't work and comes
org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing
(template: "class path resource [templates/index.html]"
Error during execution of processor
'org.thymeleaf.spring5.processor.SpringInputGeneralFieldTagProcessor'
(template: "index" - line 564, col 17)

How to assign entity to another entity in a form using ThymeLeaf?

I am working on MVC Java project and am using Spring Boot with ThymeLeaf. The issue I have is, that when creating a Task, I want to select and save a User, that will be assign to the task. I managed to do the selection correctly, but am unable to save the selected user. It always saves without the User assigned.
Here is my Task entity class:
#Entity
public class Task extends BaseEntity {
private String name;
private String description;
private Date dateOfCreation;
#DateTimeFormat(pattern = "yyyy-MM-dd")
private Date dueDate;
#ManyToOne
private User assignee;
public Task() {
this.dateOfCreation = new Date(System.currentTimeMillis());
}
}
My User entity is as follows:
#Entity
public class User extends BaseEntity {
private String username;
private String password;
#OneToMany(mappedBy = "assignee")
private List<Task> assignedTasks;
}
Where BaseEntity contains
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Version
private Integer version;
My controller function, that is responsible for my taskform.html is:
#RequestMapping("/new")
public String newTask(Model model){
model.addAttribute("task", new Task());
model.addAttribute("users", userService.getAllUsers());
return "task/taskform";
}
And my taskform.html looks like this:
<form class="form-horizontal" th:object="${task}" th:action="#{/task}" method="post">
<input type="hidden" th:field="*{id}"/>
<input type="hidden" th:field="*{version}"/>
<div class="form-group">
<label class="col-sm-2 control-label">Name:</label>
<div class="col-sm-10">
<input type="text" class="form-control" th:field="*{name}"/>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Description:</label>
<div class="col-sm-10">
<input type="text" class="form-control" th:field="*{description}"/>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Description:</label>
<div class="col-sm-10">
<input type="date" th:value="*{dueDate}" th:field="*{dueDate}" id="dueDate" />
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Select user:</label>
<div class="col-sm-10">
<select class="form-control">
<option th:field="*{assignee}" th:each="assignee : ${users}" th:value="${assignee}" th:text="${assignee.username}">
</option>
</select>
</div>
</div>
<div class="row">
<button type="submit" class="btn btn-default">Submit</button>
</div>
I am trying to pass the object from users list as the value, but it does not work Any idea, how to do it?
Showing the users works as expected:
Working frontend
Null is being assigned to assignee column, where the user should be assigned.
Null in H2 Database
EDIT
Here is my POST method from Task Controller:
#RequestMapping(method = RequestMethod.POST)
public String saveOrUpdate(Task task){
taskService.persistTask(task);
return "redirect:/task/list";
}

Spring Validation - Errors not displayed

I'm using a combination of Annotation validation and a Custom Validator
Object:
#Entity
#Table(name = "monitoringsystems")
public class MonitoringSystem {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#NotNull(message = "Name must not be empty.")#Size(min=1, message="Name must not be empty.")
private String name;
#NotNull(message = "URL must not be empty.")#Size(min=1, message="Name must not be empty.")
private String url;
#NotNull(message = "Username must not be empty.")#Size(min=1, message="Name must not be empty.")
private String username;
#NotNull(message = "Password must not be empty.")#Size(min=1, message="Name must not be empty.")
private String password;
#NotNull(message = "Confirm Password must not be empty.")#Size(min=1, message="Name must not be empty.")
#Transient
private String passwordConfirm;
CustomValidator:
#Component
public class MonitoringSystemValidator implements Validator {
#Override
public boolean supports(Class<?> type) {
return MonitoringSystem.class.isAssignableFrom(type);
}
#Override
public void validate(Object o, Errors errors) {
MonitoringSystem monitoringSystem = (MonitoringSystem) o;
if(!monitoringSystem.getPassword().equals(monitoringSystem.getPasswordConfirm())){
errors.rejectValue("passwordConfirm", "Passwords are not equal.");
}
}
}
I initialize the custom validator in my controller and set the mapping for the form and the saving method.
Controller:
#Controller
public class MonitoringSystemController {
#Autowired
private MonitoringSystemValidator monitoringSystemValidator;
#InitBinder
public void dataBinding(WebDataBinder binder) {
binder.addValidators(monitoringSystemValidator);
}
#RequestMapping("/monitoringsystem/new")
public String newMonitoringSystem(Model model, HttpServletRequest request) {
MonitoringSystem monitoringSystem = new MonitoringSystem();
model.addAttribute("monitoringSystem", monitoringSystem);
request.getSession().setAttribute("anonymization", monitoringSystem.getAnonymization());
request.getSession().setAttribute("hosts", monitoringSystem.getHosts());
return "monitoringsystem/form";
}
#RequestMapping(value = "/monitoringsystem/save", method = RequestMethod.POST)
public String save(#Valid MonitoringSystem monitoringSystem, BindingResult result, HttpServletRequest request, Model model) {
if(result.hasErrors()){
model.addAttribute("monitoringSystem", monitoringSystem);
request.getSession().setAttribute("anonymization", request.getSession().getAttribute("anonymization"));
request.getSession().setAttribute("hosts", request.getSession().getAttribute("hosts"));
return "monitoringsystem/form";
}
//more code
In a first step I only want to change the CSS of my fields (I use bootstrap) so display the errors.
Form:
<form class="form-horizontal" th:modelAttribute="monitoringSystem" th:object="${monitoringSystem}" th:action="#{/monitoringsystem/save}" method="post">
<input type="hidden" th:field="*{id}"/>
<fieldset>
<legend>New Monitoring-System</legend>
<div class="form-group" th:classappend="${#fields.hasErrors('name')} ?: 'has-error has-danger'">
<label class="col-md-4 control-label" for="textinput">Systemname</label>
<div class="col-md-5">
<input th:field="*{name}" class="form-control input-md" type="text" />
</div>
</div>
<div class="form-group" th:classappend="${#fields.hasErrors('url')} ?: 'has-error has-danger'">
<label class="col-md-4 control-label" for="textinput">URL</label>
<div class="col-md-5">
<input th:field="*{url}" class="form-control input-md" type="text" />
</div>
</div>
<div class="form-group" th:classappend="${#fields.hasErrors('username')} ?: 'has-error has-danger'">
<label class="col-md-4 control-label" for="textinput">Username</label>
<div class="col-md-5">
<input th:field="*{username}" class="form-control input-md" type="text" />
</div>
</div>
<div class="form-group" th:classappend="${#fields.hasErrors('password')} ?: 'has-error has-danger'">
<label class="col-md-4 control-label" for="textinput">Password</label>
<div class="col-md-5">
<input th:field="*{password}" class="form-control input-md" type="password" />
</div>
</div>
<div class="form-group" th:classappend="${#fields.hasErrors('passwordConfirm')} ?: 'has-error has-danger'">
<label class="col-md-4 control-label" for="textinput">Confirm Password</label>
<div class="col-md-5">
<input th:field="*{passwordConfirm}" class="form-control input-md" type="password" />
</div>
</div>
<div class="form-group">
<label class="col-md-4 control-label" for="singlebutton"></label>
<div class="col-md-4">
<a th:href="#{/monitoringsystem}" class="btn btn-default btn-small">Cancel</a> <button id="singlebutton" name="singlebutton" class="btn btn-primary btn-small">Submit</button>
</div>
</div>
</fieldset>
</form>
My validation is working correctly. The form is only saved if my fields are not null, the size is greater 1 and the password match. If not, my controller redirects me to the form.
The problem is, that my css don't change. So there must be a problem with my view-code or the errorBinding is not passed correctly to the view. But I can't find my mistake.
There was an error in my if condition which add the errorclasses. I had to change ${#fields.hasErrors('url')} ?: 'has-error has-danger' to ${#fields.hasErrors('*{name}')} ? 'has-error has-danger'

Thymeleaf registration page - Error during execution of processor 'org.thymeleaf.spring4.processor.attr.SpringInputGeneralFieldAttrProcessor'

I'm making a registration page for a website. I understand that in order for a new User to be created, an id is required, so we have the field:
<input type="hidden" th:field="{*id} />
However, when I go to the page, I get the error I mentioned in this post's title.
Here is the form in question:
<form th:action="#{/users/register}" th:object="${user}" class="form-signin" method="POST">
<h2 class="form-signin-heading">Register</h2>
<input type="hidden" th:field="*{id}" />
<label for="inputUsername" class="sr-only">Username*</label>
<input type="text" th:field="*{username}" name="username" id="inputUsername" class="form-control" placeholder="Username" required="required" autofocus="autofocus" />
<label for="inputEmail" class="sr-only">Email Address*</label>
<input type="text" th:field="*{email}" name="email" id="inputEmail" class="form-control" placeholder="Email address" required="required" autofocus="autofocus" />
<label for="inputPassword" class="sr-only">Password</label>
<input type="password" th:field="*{password}" name="password" id="inputPassword" class="form-control" placeholder="Password" required="required" />
<label for="inputConfirmPassword" class="sr-only">Confirm Password</label>
<input type="password" th:field="${confirmPassword}" name="confirmPassword" id="inputConfirmPassword" class="form-control" placeholder="Confirm password" required="required" />
<button class="btn btn-lg btn-primary btn-block" type="submit">Register</button>
</form>
Here is my UserController:
#RequestMapping("/register")
public String registerAction(Model model) {
model.addAttribute("user", new User());
model.addAttribute("confirmPassword", "");
return "views/users/register";
}
#RequestMapping(value="/register", method = RequestMethod.POST)
public String doRegister(User user) {
User savedUser = userService.save(user);
return "redirect:/"; //redirect to homepage
}
And the first part of the User entity:
#Entity
#Table(name = "users")
public class User {
// Default constructor require by JPA
public User() {}
#Column(name = "id")
#Id #GeneratedValue
private Long id;
public void setId(long id) {
this.id = id;
}
public long getId() {
return id;
}
From what I can see, there's nothing wrong here so I'm stuck.
I'm following this example: https://github.com/cfaddict/spring-boot-intro
Any ideas?
The problem is the way you have declared your id property. The field uses a reference type Long which is null. The getter uses a primitive long. When Spring accesses the id field it tries to unbox a null value causing an error. Change your domain class to be
#Entity
#Table(name = "users")
public class User {
// Default constructor required by JPA
public User() {}
#Id
#Column(name = "id")
#GeneratedValue
private Long id;
public void setId(Long id) {
this.id = id;
}
public Long getId() {
return id;
}
}
I don't know if you have a class like this
#Controller
#RequestMapping("/users")
public class MyController{
#RequestMapping("/register")
public String registerAction(Model model) {
model.addAttribute("user", new User());
model.addAttribute("confirmPassword", "");
return "views/users/register";
}
#RequestMapping(value="/register", method = RequestMethod.POST)
public String doRegister(User user) {
User savedUser = userService.save(user);
return "redirect:/"; //redirect to homepage
}
}
becouse if you don't have the #RequestMapping("/users") this is a problem becous if you dont have this annotation in you class the correct actione in the thymeleaf templace should be "#{/register}" other wise yon don't have the endpoint pubblished in other words with the methods that you posted you should have a template like this:
<form th:action="#{/register}" th:object="${user}" class="form-signin" method="POST">
<h2 class="form-signin-heading">Register</h2>
<input type="hidden" th:field="*{id}" />
.... should be as you written
<input type="password" th:field="*{confirmPassword}" id="inputConfirmPassword" class="form-control" placeholder="Confirm password" required="required" />
<button class="btn btn-lg btn-primary btn-block" type="submit">Register</button>
</form>
reading beter the your html you probably should have th:field="*{confirmPassword}" and not th:field="${confirmPassword}".
another think that in my opinion don't works is that you repeate name attribute. The my advice is don't repeate and let to thymeleaf the work of build the correct attribute for databinding.

Resources