How to Update All Fields Submitted in a Form - Spring MVC - spring

I have a form that has 10+ field inputs that will get updated by a user. On submit I need to update the object (by id) and any/all fields that were modified in the form (utilizing Spring Data JPA & Repositories). I believe I can do this by passing the #ModelAttribute as a method argument tied to my object, but I don't quite fully understand this yet. Here's my Controller
#RequestMapping(value = "/saveUser/{id}", method = RequestMethod.POST)
public String saveUser(#ModelAttribute("User") User user, #PathVariable Long id, Model model) {
user = userRepo.findOne(id); //Spring Repository method to find user by ID
// Here is where you'd set all fields of the User object including those modified in the form
model.addAttribute("user", user);
userRepo.save(user); //Updates user in database
return "success";
}
I'm looking for a method that would do the exact same thing as:
user.setName(user.getName());
user.setAddress(user.getAddress));
//etc.
Without explicitly calling each set method (the number of field inputs will grow).
Thanks mates

If you have a form and you´re using ModelAndAttribute you just need to set the id attribute to be send to the Controller
<form:input id="id" path="id"/>
<form:input id="name" path="name"/>
<form:input id="address" path="address"/>
Then in your controller you just need to do what you´re just doing
#RequestMapping(value = "/saveUser/{id}", method = RequestMethod.POST)
public String saveUser(#ModelAttribute("User") User user, #PathVariable Long id, Model model) {
user = userRepo.findOne(id); //Spring Repository method to find user by ID
// Here is where you'd set all fields of the User object including those modified in the form
model.addAttribute("user", user);
userRepo.save(user); //Updates user in database
return "success";
}
BUT, if you´re doing this request by Ajax you will need to load the again the entity User to set the new entity in your form with the new Id.
You can take a look to JQuery load to load the form element again.
Or if the save is done by HTTP Request you must refresh the page after save so you need to return again the ModelAndView with the entity user already update with an id
#RequestMapping(value = "/saveUser/{id}", method = RequestMethod.POST)
public String saveUser(#ModelAttribute("User") User user, #PathVariable Long id, Model model) {
user = userRepo.findOne(id); //Spring Repository method to find user by ID
userRepo.save(user); //Updates user in database
ModelAndView mav = new ModelAndView(yourview);
mav.setObject("user", user);
return mav;
}

Related

how to pass object between two controllers in spring mvc?

Actually I've googled to see how we can pass data from one controller to another in Spring MVC and I found that using flash attributes do the job.
It consists on declaring one controller with RequestMapping=GET and an other with RequestMapping=POST. I have tested this code in my controller and it worked.
#RequestMapping(value ="/ajouter", method = RequestMethod.POST)
public String add(#ModelAttribute User user,final RedirectAttributes redirectAttributes) {
redirectAttributes.addFlashAttribute("user", user);
return "redirect:/account";
}
and the other:
#RequestMapping(value ="/account", method = RequestMethod.GET)
public String addAccount(#ModelAttribute("user") User user,#ModelAttribute Account account) {
System.out.println(user.getId());
return "account";
}
But I have another case: I have two JSPpages:
The first will add a user into the database. I want to recuperate the id of the user just inserted to set it as a foreign key for the account (after submitting the page for adding a user, a second page for adding an account appears).
I've tested this code:
This controller will insert a user into the database and recuperate the user which has been just inserted.
#RequestMapping(value ="/addUser", method = RequestMethod.POST)
public String add(#ModelAttribute User user,final RedirectAttributes redirectAttributes) {
this.userService.insertData(user);
redirectAttributes.addFlashAttribute("user", user);
return "redirect:/account";
}
and this controller will insert an account into the database, but can not recuperate the user id of the user just inserted.
#RequestMapping(value= "/ajouterCompte", method = RequestMethod.POST)
public String addCompte(#ModelAttribute("account") Account compte,#ModelAttribute("user") User user){
System.out.println(user.getId()); //this code shows 0
System.out.println(compte.getPwd());
String hashPassword = passwordEncoder.encode(compte.getPwd());
System.out.println(hashPassword);
compte.setPwd(hashPassword);
compte.setId(user.getId());
this.compteService.insertCompte(compte);
return "redirect:/users";
}
I guess that the problem is that the 2 controllers are declared with a method method = RequestMethod.POST when it should be one GET and an other POST. But in this case how can I recuperate the id of the user so that it could be inserted into the database?
When I declare the second controller with GET, the insertion of the database fails!
Need your help please :(
The object which are set as flash attributes are only available for the first request after those have been set. (As also explained in the reference guide).
Assuming that in your account controller you have a GET based method for /account pre-populate the Account with the information at that point.
public String account(#ModelAttribute("user") User user, Model model) {
Account account = new Account();
account.setId(user.getId());
model.addAttribute("account", account);
return "account";
}
Now you can either annotate your controller with #SessionAttribute("account") to have the Account stored in the session in between requests. If so then you have to modify your POST handling method to include a SessionStatus object and after storing call setComplete() on it.
public String addCompte(#ModelAttribute("account") Account compete, SessionStatus sessionStatus){
...
sessionStatus.setComplete();
return "redirect:/users";
}
Or store the pre-filled information in hidden form fields so that it gets passed along with the following request and the Account can be reconstructed.
<form:hidden path="id" />

Neither BindingResult nor plain target object for bean name AFTER redirect

I have a form like this (in a page called add.jsp):
<form:form action="${pageContext.request.contextPath}/add" method="post" modelAttribute="addForm">
</form:form>
On GET request, i populate modelAttribute:
#RequestMapping(value ="add", method = RequestMethod.GET)
public ModelAndView add(Map<String, Object> model) {
model.put("addForm", new AddUserForm());
return new ModelAndView("add");
}
When i perform the form submitting (a POST request), i have the follow method:
#RequestMapping(value ="add", method = RequestMethod.POST)
public ModelAndView add(Map<String, Object> model, #Valid AddUserForm form, Errors errors) {
if (errors.hasErrors()) {
//model.put("addForm", new AddUserForm());
return new ModelAndView("add");
}
....
}
But i get this error: Neither BindingResult nor plain target object for bean name 'addForm' available as request attribute
My workaround is to add model.put("addForm", new AddUserForm());, the command that i have commented on POST request.... but... where is my error ?
In both case, you are returning the same view (i.e. "add") and this view contains a form with a modelAttribute="addForm" therefore the model MUST contains an object named "addForm".
If you don't wan't to populate your model with a new AddUserForm after a POST with errors, you probably should :
return another view (without the "addForm" model attribute)
or
reuse the same "addForm": model.put("addForm", form);

In MVC how is the data passed from JSP to Controller?

I'm following the Spring MVC tutorial here: http://www.tutorialspoint.com/spring/spring_mvc_form_handling_example.htm
and I'm not getting the logic how is the data passed from JSP to Controller.
I think I understand how the data is passed from the Controller to the JSP, but after the user has edited the form on the JSP how is the data passed to the Controller?
In the controller:
public String addStudent(#ModelAttribute("SpringWeb")Student student, ModelMap model)
question:
How the controller knows that from the form on the jsp Student class instance student with name, age and id are passed?
I have this example working. I have altered the example to display a list of students, but I am not able to get the list from JSP to Controller:
#RequestMapping(value = "/student", method = RequestMethod.POST)
public ModelAndView studentSave(#ModelAttribute("listOfStudents") ArrayList<Student> listOfStudents,ModelMap model)
{
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
StudentJDBCTemplate studentJDBCTemplate = (StudentJDBCTemplate) context.getBean("studentJDBCTemplate");
System.out.println("Size of listOfStudents is = " + listOfStudents.size());
...
listOfStudents.size() returns 0.
question: what am i missing here, why I can't get the list from the form on the jsp?
question: How the controller knows that from the form on the jsp
Student class instance student with name, age and id are passed?
When you submit the form you are making an HTTP (typically, POST) request to a given URL. This POST request will contain the values in
the form as request parameters. If you were not using any web framework (e.g. Spring MVC) then you would typically work directly with the Servlet API
to extract and work with these values, particularly the HttpServletRequest object.
http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html
You can try this is your application by adding the following (the Spring MVC framework will automatically pass in the request).
public String addStudent(#ModelAttribute Student student, HttpServletRequest request){
for(String key : request.getParameterMap().keySet()){
System.out.println(key + "=" + request.getParameterMap().get(key);
}
}
Now, regardless of the framework you are using the underlying mechanism does not change, the parameters are still sent in the POST request as simple Strings.
The framework however essentially adds an abstraction layer on top of this to prevent you having to write boilerplate to extract and manually work with these
parameters. So, rather than having to do the following:
public String addStudent(HttpServletRequest request){
Student student = new Student();
student.setId(Integer.parseInt(request.getParameter("id"));
student.setName(request.getParameter("name"));
....
}
you let the framework take care of it.
public String addStudent(#ModelAttribute Student student){
}
The #ModelAttribute tells the framework you want the submitted parameters to be bound to a Student instance. On submit, the framework will create a new Student
instance and, by means of reflection, (http://docs.oracle.com/javase/tutorial/reflect/) set the various fields to the corresponding HTTP params.
As for the 2nd part of your question there are numerous examples of how to bind to a Collection. See below for example:
http://viralpatel.net/blogs/spring-mvc-multi-row-submit-java-list/
Generally 'work' is done in the controller, and the results are passed to the JSP simply for display. The JSP renders to the user's browser over HTTP and then the user modifies the page and posts back to the controller.
If you're doing 'work' in the JSP that needs to be passed back to the controller before the page is sent to the user, then you should consider finding a way of doing all that in the controller.
Having said this. The model that you pass to the JSP doesn't have to contain simple objects. You can pass to the JSP an object that has methods on it that performs processing. Then in the JSP you simply call one of the methods on that object.
the controller and jsp are linked together by #ModelAttribute.
example if i want to add a new student i would first link the corresponding jsp page with the student database. like
//setup add new student form
#RequestMapping(value = "/add", method = RequestMethod.GET)
public String setStudentForm(#ModelAttribute("newStudent") Student newStudent){
return "addstudent";
}
this will set up my jsp page. In jsp page i have already declared the colums like firstname, lastname which are linked with my student model.
<form:form modelAttribute="newStudent" class="form-horizontal">
<form:input id="firstName" path="firstName" type="text"/>
<form:input id="lastName" path="lastName" type="text"/>
<input type="submit" id="btnAdd" value ="add"/>
</form>
Like this i created the ling. Now when i press submit button if will land a post request and thus following method in controller will be executed.
#RequestMapping(value = "/add", method = RequestMethod.POST)
public String processStudentForm(Model model, #ModelAttribute("newStudent") #Valid Student newStudent, BindingResult result ){
newStudent.setFirstName("gurpreet");
if(result.hasErrors()){
return "addstudent";
}
studentService.add(newStudent);
model.addAttribute("message", "Added successfully");
return "redirect:/students";
}
I can also make changes in the data like i did newStudent.setFirstName("gurpreet"); before saving the object through studentService
the #RequestMapping url is same but method has changed to POST from GET as submit button have POST submission.
(#ModelAttribute("newStudent") Student newStudent)
associates our view, Model and controller alltogether.

send an object via post

I want to make a form in which I can update my entity in my REST application. For example I have a User entity whith username and realname fields.
Do I need in my request method do something like this
#RequestMapping(value = "/admin/user/update", method = RequestMethod.POST)
public String updateHouse(#RequestBody String username, #RequestBody String realname, Model model)
??
I would prefer to make something like this
#RequestMapping(value = "/admin/house/update", method = RequestMethod.POST)
public String updateHouse(#RequestBody User user, Model model)
I mean that I want to send an object not every field. If i`ll have 20 fields in my entity I would have to add 20 params to my method. Thats not to fancy.
I`m using spring form tag
------- UPDATE
thanks for response. below diffrent entity but real case scenario that i`m trying to start
html code
<c:url var="houseUpdateLink" value="/admin/house/update" />
<form:form method="post" commandName="house" action="${houseUpdateLink}">
<form:input path="house.title" />
<input type="submit" value="send" />
</form:form>
controller method
#RequestMapping(value = "/admin/house/update", method = RequestMethod.POST)
public String updateHouse(#RequestBody House house, Model model) {
model.addAttribute("step", 3);
logger.info("test: " + house.getTitle());
return "houseAdmin";
}
and i receive
HTTP Status 415 -
type Status report
message
description The server refused this request because the request entity is in a format not supported by the requested resource for the requested method ().
You don't need #RequestBody here. Spring will automatically bind the form fields to the House class without it, i.e.
#RequestMapping(value = "/admin/house/update", method = RequestMethod.POST)
public String updateHouse(House house, Model model) {
In the Spring form tags, you can probably do user.username, and pass the User object as a param.

Session Handling in Spring MVC 3.0

I am using session.setAttribute to store user object after login. In next controller, I have #SessionAttribute for the same user and #ModelAttribute for same object to be used in the method mapped to a RequestMapping. After login if I click any link in the user home page it give
HttpSessionRequiredException: Session attribute '' required - not found in session
I am not sure what I am doing wrong. I went through many article and question in this site as well but could find any solution. The user object which I am storing in session stores user's account details which are required in all the controller to get different information from DB. I using SessionAttribute is wrong should I use HttpSession instead in all the controller and get the object from session manually or there is a proper way to handle in spring 3.0. Please note that this user object is not backing any form just login, but contains many other details.
As help would be good.
Have a look at my (non-perfect) use of session data:
#Controller
#SessionAttributes("sharedData")
public class RegistrationFormController {
#Autowired
private SharedData sharedData; // bean with scope="session"
#RequestMapping(value = {"/registrationForm"}, method = RequestMethod.GET)
public ModelAndView newForm() {
final ModelAndView modelAndView = new ModelAndView("registrationForm");
modelAndView.addObject("registrationForm", new RegistrationForm());
// I want to render some data from this object in JSP:
modelAndView.addObject("sharedData", sharedData);
return modelAndView;
}
#RequestMapping(value = {"/registrationForm"}, method = RequestMethod.POST)
public String onRegistrationFormSubmitted(HttpServletRequest request,
#ModelAttribute("registrationForm") RegistrationForm registrationForm, BindingResult result) {
if (result.hasErrors()) {
return "registrationForm";
}
// Perform business logic, e.g. persist registration data
return "formSubmitted";
}
}

Resources