What is the proper way to handle editing objects in Spring MVC. Let's say I have user object:
public class User {
private Integer id;
private String firstName;
private String lastName;
//Lets assume here are next 10 fields...
//getters and setters
}
Now in my controller I have GET and POST for url: user/edit/{id}
#RequestMapping(value = "/user/edit/{user_id}", method = RequestMethod.GET)
public String editUser(#PathVariable Long user_id, Model model) {
model.addAttribute("userForm", userService.getUserByID(user_id));
return "/panels/user/editUser";
}
#RequestMapping(value = "/user/edit/{user_id}", method = RequestMethod.POST)
public String editUser(#Valid #ModelAttribute("userForm") User userForm,
BindingResult result, #PathVariable String user_id, Model model) {
if(result.hasErrors()) {
User user = userService.getById(user_id);
user.updateFields(userForm);
}
userService.update(user);
}
Now the question is do I really need to get my user from database in POST method and update every field one by one in some update method or is there better way for that?
I am thinking about using #PathVariable for User and get User from database with converter and then in some way inject parameters from POST method into that object automatically. Something like this:
#RequestMapping(value = "/user/edit/{user}", method = RequestMethod.POST)
public String editUser(#Valid #PathVariable("user") User userForm,
BindingResult result, Model model)
But when I try this I got error with BindingResults:
java.lang.IllegalStateException: An Errors/BindingResult argument is expected to be declared immediately after the model attribute, the #RequestBody or the #RequestPart arguments
Is there any easy way to create controller to handle objects editing or do I need to copy fields which could change one by one??
By the way, I can't use SessionAttributes because it causes problems for multiple tabs.
I believe you are sending "userForm" as a model attribute. If so try with following pattern,
#RequestMapping(value = "/user/edit/{user_id}", method = RequestMethod.POST)
public String editUser(#PathVariable String user_id, #Valid #ModelAttribute("userForm") User userForm,
BindingResult result, Model model)
Thanks
You keep user id in a input hidden inside your edit form.
#RequestMapping(value = "/user/edit", method = RequestMethod.POST)
public String editUser(#Valid #ModelAttribute("userForm") User userForm,
BindingResult result,Model model){
if(result.hasErrors()){
User user = userService.getById(userForm.getId());
user.updateFields(userForm);
}
userService.update(user);
return "redirect:.......";
}
Related
I have a user entity that has many attributes (some fields not shown here):
#Entity
public class User {
#OneToOne(cascade = ALL, orphanRemoval = true)
private File avatar; // File is a custom class I have created
#NotEmpty
#NaturalId
private String name;
#Size(min = 6)
private String password;
#Enumerated(EnumType.STRING)
private Role role;
}
In my thymeleaf template I have a form that submits username, password and avatar (MultipartFile). Now in my controller instead of these parameters...
#PostMapping("/register")
public String register(#RequestParam String username,
#RequestParam String password,
#RequestParam MultipartFile avatar) { ...
...I want to use #ModelAttribute #Valid User user. My problem is that:
password first should be encrypted then passed to the user entity,
bytes[] from MultipartFile should be extracted then stored in user entity (as a custom File object),
some other fields such as Role should be set manually in the service class.
How can I take advantage of #ModelAttribute?
Instead of trying to shoehorn everything into your User class, write a UserDto or UserForm which you can convert from/to a User. The UserForm would be specialized for the web and converted to a User later on.
The conversions you are talking about should be done in your controller (as that is ideally only a conversion layer before actually talking to your business services).
public class UserForm {
private MultipartFile avatar;
#NotEmpty
private String username;
#Size(min = 6)
#NotEmpty
private String password;
public UserForm() {}
public UserForm(String username) {
this.username = username;
}
static UserForm of(User user) {
return new UserForm(user.getUsername());
}
// getters/setters omitted for brevity
}
Then in your controller do what you intended to do (something like this):
#PostMapping("/register")
public String register(#ModelAttribute("user") UserForm userForm, BindingResult bindingResult) {
if (!bindingResult.hasErrors()) {
User user = new User();
user.setName(userForm.getUsername());
user.setPassword(encrypt(userForm.getPassword());
user.setAvataor(createFile(userForm.getAvatar());
userService.register(user);
return "success";
} else {
return "register";
}
}
This way you have a specialized object to fix your web based use cases, whilst keeping your actual User object clean.
Maybe you can just use a setter to make all these actions. When Spring is mapping data to fields, and you have setters in the entity, it will use them to pass data. You can preprocess data in this way and set final values to fields.
#PostMapping("/register")
public String register(#ModelAttribute User user, Model model) { // remember if You have another name for parameter and backing bean You should type this one #ModelAttribute(name="userFromTemplate") User user
encryptPassword(user.getPassword); //remember that is sample code, You can do it however You want to
extractBytes(user.getAvatar); //same here
user.setRole(manuallySetRole);
model.addAttribute("user", user);
return "success"; // here u can redirect to ur another html which will contain info about ur user
} else
return "redirect:sorry";
}
encryptPassword(String password) { ... }
same for the rest methods
Here i give You sample code how to use #ModelAttribute in your example. If You have questions feel free to comment.
How to get the value has been set from GET method? How to pass the value to POST method? Should I declare global variable?
EDIT: What I want is doing something on GET method and display a textbox on jsp if setdisplayBox = true. When the user POST the form, the setdisplayBox should be true too and return the same jsp without redirect
#RequestMapping(method = RequestMethod.GET)
public String getSuccess(ModelMap model, #ModelAttribute("user") User user, HttpServletRequest request)
{
String boxDisplay = "True";
user.setdisplayBox(boxDisplay);
return "success";
}
#RequestMapping(method = RequestMethod.POST)
public String resetPassword(HttpServletRequest request, ModelMap model, #ModelAttribute("user") User user, ModelMap modelMap)
{
user.setdisplayBox(user.getdisplayBox()); //how to get value has been set above?
return "success";
}
Set a global variable for the user.
private User userAccount;
#RequestMapping(method = RequestMethod.GET)
public String getSuccess(ModelMap model, #ModelAttribute("user") User user, HttpServletRequest request)
{
String boxDisplay = "True";
user.setdisplayBox(boxDisplay);
userAccount = user; //load user in to global var
return "success";
}
#RequestMapping(method = RequestMethod.POST)
public String resetPassword(HttpServletRequest request, ModelMap model, #ModelAttribute("user") User user, ModelMap modelMap)
{
user.setdisplayBox(userAccount.getdisplayBox());
userAccount = null; //reset it to something to make sure you are loading it to another user later.
return "success";
}
I am using #RequestParam to catch the front-end user input and pass it to the back end through controller and save it to the database.
So far controller handles the request like this:
#RequestMapping(value = "/myURL", method = RequestMethod.POST)
public String saveCustomer(
#RequestParam("customerFirstName") String customerFirstName,
#RequestParam("customerLastName") String customerLastName,) {
Customer customer = customerService.saveCustomer(
customerFirstName, customerLastName);
return null;
}
Well I guess this is fine when I only have two #RequestParam for two arguements, but I am facing some table that has more than 10 params, I think by using #RequestParam is apparently not realistic, is there another around this?
You can save the customer directly.
#RequestMapping(value = "/myURL", method = RequestMethod.POST)
public String saveCustomer(Customer customer) {
customerService.saveCustomer(customer);
return null;
}
Spring can databind POJOs as long as you have a no-args constructor and setters for your properties.
You should create a provider to use JSON, so you can send complex Java\JS objects and not just primitives.
If you are going to deal with multiple #RequestParam you can always opt for a bean class approach.
Controller:
#RequestMapping(value = "/myURL", method = RequestMethod.POST)
public String saveCustomer(
#RequestBody Customer customer) {
return null;
}
Bean:
public class Customer{
private String customerFirstName;
private String customerLastName;
//constructors, getters and setters
}
Check this link out for more info on #RequestBody
My project is java spring mvc. I want data list for front end with specific employee.
My controller is
#RequestMapping(value = "/home", method = RequestMethod.GET)
public String setUp(Model model, HttpServletRequest request, #ModelAttribute employeedetail employeedetail) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String empNumer = auth.getName();
employeedetail employee = employeeServices.getEmployee(empNumer);
request.getSession().setAttribute("userId", employee.getEmpNumer());
return "home";
}
In here I got log in user number so I want get list of detail of employee as a array list.
I have a Contact object that I put in the request, this object is modified in the
form and then get the modified object. I would like the object that is back is the same object that you send, you keep the value of the attributes that were not in the form.
class Contact{
private String name; // this attributes will be modified
private String lastName;
private Long id;
private Date created; // this atributes will not be modified
// getters and setters ....
}
#RequestMapping(value = "/{id}/edit", method = RequestMethod.GET)
public String updateContact(#PathVariable("id") Long id, Model model) {
Contact c = contactDao.get(id);
model.addAttribute("contact", c);
return "contact/form";
}
#RequestMapping(value = "/{id}/edit", method = RequestMethod.POST)
public String update(#PathVariable("id") Long id, #Valid #ModelAttribute Contact contact, BindingResult result, Model model) {
// The contact I get here I want to keep the original attributes of the
// object sent, and have the changes in the fields shown on the form. is that possible?
return "redirect:/contact";
}
<form:form action="${pageContext.servletContext.contextPath}/tags/create" commandName="contact">
<form:input path="name"/>
<form:errors path="name" cssClass="formError"/>
<form:input path="lastName"/>
</form:form>
I do not want to use hidden fields to maintain the value of the attributes that will not be changing
If you only want some of the fields to be handled in a form, make a new class - ContactDTO that contains only them, and then manually (or through reflection) copy them to the original Contact object (which you load by id from the DB)
I found the solution to the problem by stating the contact object as an object that lives in the session
#Controller
#RequestMapping("/contact")
#SessionAttributes("contact")
public class ContactController {
....
....
#RequestMapping(value = "/{id}/edit", method = RequestMethod.GET)
public String updateContact(#PathVariable("id") Long id, Model model) {
Contact c = contactDao.get(id);
model.addAttribute("contact", c);
return "contact/form";
}
#RequestMapping(value = "/{id}/edit", method = RequestMethod.POST)
public String update(#PathVariable("id") Long id, #Valid #ModelAttribute Contact contact, BindingResult result, Model model) {
contactDao.update(contact);
return "redirect:/contact";
}
}
What is your persistence framework? is it JPA or Hibernate? If so, annotate the field with #Column(updatable=false)