I’ve an issue with update of user data in Spring MVC app.
So, I have user and I have FORM filled with data from JSP. Now data from FORM overrides all fields of user data with null, except entered in jsp.
In another case – user’s data overrides form’s data.
Help me, please, to do it correctly. I’ve tried a lot of variants but nothing works.
#RequestMapping(value = "/edit", method = RequestMethod.GET)
public ModelAndView updateView(#ModelAttribute(value = "updateForm")
HttpSession session) {
User user = (User) session.getAttribute("user");
UserForm updateForm = new UserForm();
updateForm.setUser(user);
return new ModelAndView("profileupdate", "updateForm", updateForm);
}
#RequestMapping(method = RequestMethod.POST)
public String updateUserProcess(#ModelAttribute(value = "updateForm")
UserForm updateForm,
BindingResult result, Model model,
HttpSession session) {
User user = (User) session.getAttribute("user");
model.addAttribute("updateForm", updateForm);
if (result.hasErrors()) {
return "profileupdate";
}
if (!updatingUser(updateForm, model, user))
model.addAttribute("errorMsg", "Login or Email is already in use!");
return "profileupdate";
}
return "updated";
}
private boolean updatingUser(UserForm updateForm, Model model, User user) {
fillForm(updateForm, user);
user = updateForm.getUser();
//Another case
//user = updateForm.getUser();
//fillForm(updateForm, user);
return userService.updateUser(user);
}
private void fillForm(UserForm updateForm, User user) {
updateForm.setUserId(user.getUserId());
updateForm.setLogin(user.getLogin());
updateForm.setPassword(user.getPassword());
updateForm.setEmail(user.getEmail());
}
}
** UserForm class**
public class UserForm {
private Integer userId;
private String login;
private String name;
private String password;
private String email;
public UserForm() {
}
public User getUser() {
User user = new User();
user.setUserId(userId);
user.setLogin(login);
user.setPassword(password);
user.setName(name);
user.setEmail(email);
return user;
}
public void setUser(User user) {
this.userId = user.getUserId();
this.login = user.getLogin();
this.password = user.getPassword();
this.name = user.getName();
this.email = user.getEmail();
………………………….
getters and setters
}
This is my DAO and Service
#Override
public boolean updateUser(User user) {
return userDao.updateUser(user);
}
#Override
#Transactional
public boolean updateUser(User user) {
if (isUserExists(user)) {
return false;
}
currentSession().update(user);
return true;
}
Updade.jsp
<sf:form name="login"
method="POST"
action="${app}/edit"
modelAttribute="updateForm"
enctype="application/x-www-form-urlencoded">
<label for="login">Login:</label><br>
<input name="login" id="login" type="text" value=""/> <br>
<sf:errors path="login" cssClass="error"/><br>
<br><label for="password">Password:</label>
<br><input name="password" id="password" type="password" value=""/>
<br>
<sf:errors path="password" cssClass="error"/><br>
<br> <input type="submit" name="submit" value="Update"/>
</sf:form>
It would be very hard for spring or hibernate to guess what values are null because user wants them to be null and what are null because they do not have to be touched. You as the progammer have to supply a fully completed object.
There are two common ways to do that :
you suppose null fields should be left untouched and modify fillform accordingly :
if (updateForm.getUserId().isEmpty()) { updateForm.setUserId(user.getUserId()); }
...
you prefil your form with current User value in the get that precedes the post (more common unless you need a post without the get part before)
EDIT
To prefill the form (the jsp part seems to be fine) your controller should put a filled UserFormin the model in the GET method.
#RequestMapping(method = RequestMethod.GET)
public String updateView(#ModelAttribute(value = "updateForm")
UserForm updateForm,
HttpSession session) {
User user = (User) session.getAttribute("user");
updateForm.setUser(user);
return "profileupdate";
}
with updateForm being implicitely in model due to the #ModelAttribute annotation, or
#RequestMapping(method = RequestMethod.GET)
public ModelAndView updateView(HttpSession session) {
updateForm = new UserForm();
User user = (User) session.getAttribute("user");
updateForm.setUser(user);
return new ModelAndView("profileupdate", "updateForm", updateForm);
}
I also removed the value="/edit" because it is not on updateUserProcess and I assumed the "/edit" was allready established on controller.
Well, main problem was in the path on JSP. Instead of handle request through controller I've set just a link to the page. So, advice - be careful and attentive with mappings.
WRONG VERSION OF LINK
<form name="redaction"
action="${pageContext.request.contextPath}/updatepage.jsp"
method="GET"
enctype="application/x-www-form-urlencoded">
<input type="submit" name="submit" value="Redaction"/>
</form>
RIGHT VERSION OF LINK
<form name="redaction"
action="${pageContext.request.contextPath}/edit"
method="GET"
enctype="application/x-www-form-urlencoded">
<input type="submit" name="submit" value="Redaction"/>
</form>
And controller
#RequestMapping(value = "/edit", method = RequestMethod.GET)
public String updateView(UserForm userForm,
HttpSession session,
ModelMap model) {
User user = (User) session.getAttribute("user");
userForm.setUser(user);
model.addAttribute("userForm", userForm);
return "profileupdate";
}
#RequestMapping(value = "/edit.do", method = RequestMethod.POST)
public String updateUserProcess(#ModelAttribute(value = "userForm")
UserForm userForm,
BindingResult result, Model model,
HttpSession session) {
User user = (User) session.getAttribute("user");
session.getAttribute("userForm");
model.addAttribute("userForm", userForm);
userValidator.validate(userForm, result);
if (result.hasErrors()) {
logger.error("Validation error");
return "profileupdate";
}
if (!updatingUser(userForm, model, user)) {
logger.error("User update error!");
logger.error("Login or Email is already in use!");
model.addAttribute("errorMsg", "Login or Email is already in use!");
return "profileupdate";
}
logger.info("User updated!");
return "newuserprofile";
}
Have you checked the values of user.getUserID,user.getLogin(),user.getPassword(),user.getEmail()
in the following segment of code? Is it null or the data you recieved in the model object User?
updateForm.setUserId(user.getUserId());
updateForm.setLogin(user.getLogin());
updateForm.setPassword(user.getPassword());
updateForm.setEmail(user.getEmail());
Please post the code for userService.updateUser(user) so that we can understand more.
Related
How to validate an integer type value in a form input using the BindingResult in spring?
When I try to validate the input I am getting an exception.I have gone through many posts but did not understand the concept completely.
The problem here isn't with validation, it is with the data binding. Data binding happens before validation, and validation is only invoked when all fields have been converted and bound correctly. In my case, the binding step is failing.Please correct me if I am wrong here.
<div class="form-group">
<div class="col-sm-9">
<label th:if="${#fields.hasErrors('age')}" th:errors="age"
class="validation-message"></label>
<input type="number" th:field="*{age}"
placeholder="Age" class="form-control" />
</div>
</div>
Code to map the request
#RequestMapping(value = "/registration", method = RequestMethod.POST)
public ModelAndView createNewUser(#Valid User user, BindingResult bindingResult) {
ModelAndView modelAndView = new ModelAndView();
User userExists = userService.findUserByEmail(user.getEmail());
if (userExists != null) {
bindingResult
.rejectValue("email", "error.user",
"There is already a user registered with the email provided");
}
if (bindingResult.hasErrors()) {
modelAndView.setViewName("registration");
} else {
userService.saveUser(user);
modelAndView.addObject("successMessage", "User has been registered succssfully");
modelAndView.addObject("user", new User());
modelAndView.setViewName("registration");
}
return modelAndView;
}
User.class
#Column(name = "age")
#NotEmpty(message = "*age is mandatory")
private Integer age;
Exception:
javax.validation.UnexpectedTypeException: HV000030: No validator could be found for constraint 'javax.validation.constraints.Size' validating type 'java.lang.Integer'. Check configuration for 'age'
at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.throwExceptionForNullValidator(ConstraintTree.java:229) ~[hibernate-validator-5.2.4.Final.jar:5.2.4.Final]
at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.getConstraintValidatorNoUnwrapping(ConstraintTree.java:310) ~[hibernate-validator-5.2.4.Final.jar:5.2.4.Final]
at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.getConstraintValidatorInstanceForAutomaticUnwrapping(ConstraintTree.java:244) ~[hibernate-validator-5.2.4.Final.jar:5.2.4.Final]
Not empty constraint is used for Strings and collections.
https://docs.jboss.org/hibernate/beanvalidation/spec/2.0/api/javax/validation/constraints/NotEmpty.html.
To verify if an Integer has some value, #NotNull constraint should be used.
I'm trying to create update form using Spring mvc. Once user click the update button it shows the current values in input tags. When I edit the current values and hit the submit button it shows the following error.
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver handleTypeMismatch
WARNING: Failed to bind request element: org.springframework.beans.TypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'int'; nested exception is java.lang.NumberFormatException: For input string: "updateStudent"
home.jsp
<form:form method="GET" action="updateStudent" commandName="selectedUser">
<table>
<tr>
<form:input path="userID" placeholder="userID"
value="${selectedUsers.userID}" />
<form:input path="userName" placeholder="userName"
value="${selectedUsers.userName}" />
<form:input path="password" placeholder="password"
value="${selectedUsers.password}" />
</tr>
<tr>
<td colspan="2"><input type="submit" value="Submit" /></td>
</tr>
</table>
</form:form>
controller.java
#RequestMapping(value = "updateStudent", method = RequestMethod.GET)
public String updateStudent(#ModelAttribute("selectedUser") User user,
ModelMap model) throws Exception {
Student student = new Student();
System.out.println("come here helloController()");
model.addAttribute("userID", user.getUserID());
model.addAttribute("userName", user.getUserName());
model.addAttribute("password", user.getPassword());
model.addAttribute("student",student);
model.addAttribute("user",user);
System.out.println("student id is " + user.getUserID());
// model.addAttribute("userI", user);
if (user.getUserID() == 0) {
model.addAttribute("error", "please enter id");
return "home";
}
if (user.getUserName() == null) {
model.addAttribute("error", "please enter getUserName");
return "home";
}
if (user.getPassword() == null) {
model.addAttribute("error", "please enter getPassword");
return "home";
}
return "home";
}
User.java
public class User {
int userID;
String userName;
String password;
public int getUserID() {
return userID;
}
public void setUserID(int userID) {
this.userID = userID;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
I need to pass the updated values to controller. Hope you got the question. Please help to figure out the issue.
Your model class User have the attribute int id which is primitive type data. change it to Interger object.
Integer userID;
along with getter and setter. everything will be fine I think.
I've a simple form
<form:form commandName="user">
<form:input path="name"/>
<input type="submit" value="Go" />
</form:form>
through which I get data and save in Database using
#Controller
public class UserController {
#Autowired
private UserService userService;
#ModelAttribute("user")
public User construct(){
return new User();
}
#RequestMapping("/users")
public String users(Model model) {
model.addAttribute("users", userService.findAll());
return "users";
}
#RequestMapping("/register")
public String showRegiteratinForm() {
return "user-register";
}
#RequestMapping(value = "/register", method = RequestMethod.POST)
public String doRegiteratin(#ModelAttribute("user") User user) {
userService.save(user);
return "users";
}
}
and want to show all users
<P> The User on the server are </P>
<c:forEach items="${users}" var="user">
<p>${user.name}</p>
</c:forEach>
on users page using
#Service
public class UserService implements IDbCrud<User>{
#Autowired
private UserRepository userRepository;
#Override
public List<User> findAll() {
// TODO Auto-generated method stub
return userRepository.findAll();
}
#Override
public void save(User obj) {
// TODO Auto-generated method stub
userRepository.save(obj);
}
}
data is inserted, but issue is no data/user is showed at user page and if I change form to this
<form:form commandName="user" action="/context/users">
<form:input path="name"/>
<input type="submit" value="Go" />
</form:form>
I get all users which are already saved but the last one is not saved. How to do the trick for get this work accordingly.
I am new to Thymeleaf and am trying to check for a null attribute in my template
<form th:action="#{/saveUser/__${user.id}__}" th:object="${user}" method="post">
The form submits fine if I am editing an existing user with an id already defined, however using the same form to add a new user I get the following
HTTP Status 400 - http://localhost:8080/myApp/saveUser/null"
My controller:
#RequestMapping(value = "/saveUser/{id}", method = RequestMethod.POST)
public String saveUser(#ModelAttribute("user") User user, #PathVariable Long id, Model model) {
model.addAttribute("user", user);
userRepo.save(user); //JPA Repo
return "success";
}
My thought is if I can check for the null id I can plug in a unique one somehow. Better yet, if I could make use of the #GeneratedValue set on my User object's ID then I think I'd be in good shape
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
EDIT Including my user edit form method which returns the same form but pre-populated with the User's model Attributes
#RequestMapping(value = "/edit/user/{id}", method = RequestMethod.GET)
public ModelAndView getUserEditForm(#PathVariable Long id, Model model){
ModelAndView mav = new ModelAndView("userForm");
User user = userRepo.findOne(id);
mav.addObject("userForm", user);
return mav;
}
EDIT 2 Including my whole form (ID is "hidden" from user)
<form th:action="#{/saveUser/__${user.id}__}" th:object="${user}" method="post">
<input id="id" type="hidden" th:field="*{id}" />
<input id="id" type="text" th:field="*{name}" />
<input id="id" type="text" th:field="*{address}" />
<button id="save">Save</button>
</form>
As per discussion assuming that the following method is the one you call which should populate the user object and thus form fails on submission:
#RequestMapping(value = "/saveUser/{id}", method = RequestMethod.POST)
public String saveUser(#ModelAttribute("user") User user, #PathVariable Long id, Model model) {
model.addAttribute("user", user);
userRepo.save(user); //JPA Repo
return "success";
}
The reason that method doesn't work is because you are potentially passing an empty user object to begin with.
To remediate this you need to implement checks to ensure object is not null prior to calling the page.
one solution could be:
#RequestMapping(value = "/saveUser/{id}", method = RequestMethod.POST)
public String saveUser(#ModelAttribute("user") User user, #PathVariable Long id, Model model) {
userRepo.save(user); //JPA Repo
if(user == null) // check if user object is empty
user = new User(); // if user is empty, then instantiate a new user object
model.addAttribute("user", user);
return "success";
}
The above will ensure that when you passing user object to the model, it is always available.
I've temporarily resolved this by creating a new #Controller to accept "null" at the end of the #RequestMapping (which kind of makes sense when creating a new user with a dynamic ID?), and just reimplementing the same logic. So when this is called
http://localhost:8080/myApp/saveUser/null"
It is mapped to my new Controller
#RequestMapping(value = "/saveUser/null", method = RequestMethod.GET)
public ModelAndView saveUser(#ModelAttribute("user") User user, Model model){
model.addAttribute("user", user);
userRepo.save(user);
return "success";
}
I have a form
<form method="POST" action="/user/${id}">
<input type="text" name="id" value="${id}" placeholder="Input Id">
<button>Get User</button>
</form>
How to pass id to controller?
#RequestMapping (value = "/user/{id}", method = RequestMethod.POST)
public String getStudent(#PathVariable ("id") Integer id, Model model){
User savedUser = userRepository.get(id);
model.addAttribute("user", savedUser);
return "user";
}
You could do this way , consider i am passing the ${id} value through the query string
Get User
And in your controller,
#RequestMapping ("user")
public String getStudent(#RequestParam Integer id, Model model){
User savedUser = userRepository.get(id);
model.addAttribute("user", savedUser);
return "user";
}
Hope this helps !