Declaring session model attribute as:
#SessionAttributes ("customer")
Controller code is basically to modify the customer object :
#RequestMapping(value="/testlink", method=RequestMethod.GET)
public String testLinkHandler(ModelMap modelMap){
customerDao.getCustomer(111);
modelMap.put("customers", customerDao.getCustomers());
Customer cust = customerDao.getCustomer(115);
if (cust == null){
cust = new Customer();
}
modelMap.put("customer", cust);
return "testlink";
}
#RequestMapping(value="/testlink", method=RequestMethod.POST)
public String testLinkHandler(#ModelAttribute Customer customer){
customerDao.save(customer);
return "redirect:/testlink";
}
With above code in POST method Customer object is loaded from session & posted new customer name with proper id and hence Editing the Customer works perfectly and updates DB with modified customer Name.
But the moment I change the model variable name and the #SessionAttribute name from "customer" to say "customerModel" or "customer_model" or "model" it doesn't work anymore and above code inserts a new record in DB.
So the question is, Is there a naming convention that needs to be followed here?
public String testLinkHandler(#ModelAttribute Customer customer){ ... }
This method expects an object named customer to be available for binding. When using #ModelAttribute without attributes Spring MVC tries to deduce the name of the model attribute from the method argument name.
Now if you decide to rename your model attribute you either have to
Rename the method argument accordingly
Supply the name to the #ModelAttribute.
As I wouldn't suggest option 1, that leaves option 2.
public String testLinkHandler(#ModelAttribute("your-model-name-here") Customer customer){ ... }
With #SessionAttribute, Spring obtains an instance of model attribute from the session.
Hence model attribute field name should match with the session attribute name,in this case customer
If you require changing of the attribute name then you can use :
#SessionAttributes (types = {Customer.class})
Now, whenever you put your modelClass of the type Customer in spring Model then it would automatically be set in the session.
Related
#PostMapping("/regist")
public String regist(Person person, Model model) {
Person p = new Person("name", "age");
model.addAttribute("person", p); //add person to model
model.addAttribute("hobby", "reading);
return "redirect:/info";
}
#GetMapping("/info")
public String info() {
return "result";
}
Why (person) model.addAttribute("person", p) not appended to url like (hobby) when redirecting?
Model attributes are not used for redirects. Have a look at RedirectAttributes instead.
A specialization of the Model interface that controllers can use to
select attributes for a redirect scenario. Since the intent of adding
redirect attributes is very explicit -- i.e. to be used for a redirect
URL, attribute values may be formatted as Strings and stored that way
to make them eligible to be appended to the query string or expanded
as URI variables in org.springframework.web.servlet.view.RedirectView.
This interface also provides a way to add flash attributes. For a
general overview of flash attributes see FlashMap. You can use
RedirectAttributes to store flash attributes and they will be
automatically propagated to the "output" FlashMap of the current
request.
Example usage in an #Controller:
#RequestMapping(value = "/accounts", method = RequestMethod.POST)
public String handle(Account account, BindingResult result, RedirectAttributes redirectAttrs) {
if (result.hasErrors()) {
return "accounts/new";
}
// Save account ...
redirectAttrs.addAttribute("id", account.getId()).addFlashAttribute("message", "Account created!");
return "redirect:/accounts/{id}";
}
A RedirectAttributes model is
empty when the method is called and is never used unless the method
returns a redirect view name or a RedirectView.
After the redirect, flash attributes are automatically added to the
model of the controller that serves the target URL.
At present what I have is one view in HTML for entering Person's details and Company's details. I am using spring MVC framework restful.
I create json and send request using Ajax to Restcontroller.based on URL pattern create method is called .e.g. json is
{"name":"rohit","address":"Pune","company":"ABC"}
Here above name and address belong to person bean and company belongs to company bean. I want the json value bind to their respective bean. How to do it? I have tried the code below but I know it won't work.
#Requestmapping(value="/createperson",method=method.post)
public #Responsebody String createperson(#Requestbody person,#Requestbody company)
{
//Some code to save
}
I have a form, which will input the person's details and the person's company details.
What I want is that when this form is submitted, some of its fields are bound to Person object properties and some to Company object properties. How can this be done? And how to do validation for json value and send all errors as json responsive again back if there are any errors.
You can only have one #RequestBody. Spring then looks at the content-type header and finds an appropriate HttpMessageConverter which will read the entire http entity body (input stream) into a single object.
What you have basically done is try to merge Person and company into a single JSON object, and thereby flattened the structure. If you want spring to handle that, you need to create a new object with the same (flat) hierarchy. Or you need to create a wrapper class PersonAndCompany which contains both a Person and a Company, and then change the JSON to match the structure, so it looks like this.
{
"person" : {
"name":"rohit",
"address":"Pune"
},
"company" : {
"name":"ABC"
}
}
you should do like this if you are using relationship between Person and Company otherwise it is better to use single bean instead of two.
#ResponseBody
#RequestMapping(value = "/createperson", method=RequestMethod.POST ,consumes=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Person> createperson(#RequestBody Person person) {
if(error found ){
Person p new Person();
p.setError(" error message ");
return new ResponseEntity<Person>(p,HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<Person>(person,HttpStatus.OK);
}
public class Person {
private String name;
private String address;
Company company;
String error;
--- setters getters
}
public class Company {
String compName;
--- setters getters
}
input json
{"name":"person name ","address":"person address ","company":{"compName":"company name"}}
I am using the spring mvc3,and I found that I am confused with the some concepts,so I make a summary, and post it here.
Data binding.
I use struct2 before where if I have a bean in the action(i..e,named 'Person'),then when the following url is requested:
http://localhost:8080/app/form?person.id=1&person.name=xxx&pet.name=yy
Then a new instance of Person will be created and the parameters '1' and 'xxx' will be populated to this instance.
And a new instance of Pet will be created,and the name 'yy' will be populated to this bean.
This is the only way for data binding in struct2:
You specify the beanname.property in your request.
Now in spring mvc3,I know one way to binding the data:
http://localhost:8080/app/form?id=1&name=xxx&other=yy
#RequestMapping('form')
public String form(Person person,BindingResult result){
//now I get the person
}
Even it work,but I have some questions:
1) I have three parameters in the request url "name",'pass','other',how does spring know which parameters should be populated to my bean(model).
2) how about if I want to bind parameters to more than one model like the example in struct2(both person and pet)?
Furthermore,what does the modelAttribute and commandName attribute in the spring tag "form" mean?
2. Validate
For bean(ignore the getter and setter in the example):
import javax.validation.constraints.NotNull;
public class Bean {
#NotNull(message="id can not be null")
private int id;
}
I use this controller:
#RequestMapping("val")
public #ResponseBody
String validate(#Valid Bean bean, BindingResult result) {
if (result.hasErrors()) {
return result.getAllErrors() + "";
} else
return "no error";
}
I found that only the 'NotEmpty' works.
Since if I use this url:
/val?name=xxx ==> no error !! // id is null now,why it does not has error?
3. The type conversion.
Take this url for example:
http://localhost:8080/app/form?id=1&name=xxx&other=yy
Obviously it will throw 'NumberFormatException' if I set the id to string value.
Then where to catch this exception? If in the controller,then I have to write so many try-catch block in each controller.
Furthermore,the the type conversion exception do not limited to only 'NumberFormatException',it may be 'dataformaterrorexception' and etc.
But they all belongs to the type conversion,how to handle the using a common solution?
I'd like to use the autowiring magic of #ModelAttribute for obtaining and passing around reference data. The problem in doing this is that anything added to the model with #ModelAttribute is assumed to be a form backing object and then is bound to the request, potentially modifying the objects.
I simply want them added to the model for the view's reference and to be able to use the parameter level #ModelAttribute to wire objects into methods annotated with #RequestMapping. Is there a way to accomplish this without some verbose #InitBinder method?
for example:
#ModelAttribute("owner")
public Person getOwner(#PathVariable("ownerId") Integer ownerId){
return getOwnerFromDatabaseById(ownerId);
}
#RequestMapping("/{ownerId}/addPet.do")
public ModelAndView addPet(HttpServletRequest request, #ModelAttribute("owner") Person owner){
String name = ServletRequestUtils.getStringParameter(request, "name");
Pet pet = new Pet();
pet.setName(name);
pet.setOwner(owner);
saveToDatabase(pet);
}
A trivial example where a pet is added to an owner. I'd like to have the owner placed in the model to be used by the view, and i'd also like to make use of autowiring the parameter in addPet(). Assume both Pet and Person have a member name. In this case, owner will automatically get bound to the request, setting its name to the pet's name. How can this be avoided?
I think you are doing it wrong, in this case the #ModelAttribute should be Pet - that is what should be used as the form backing object. To get he owner populated automatically based on the ownerId you can register a property editor for the Owner class that will have the logic you currently have in the getOwner method.
I'm working with Spring MVC and I'd like it to bind a a persistent object from the database, but I cannot figure out how I can set my code to make a call to the DB before binding. For example, I'm trying to update a "BenefitType" object to the database, however, I want it to get the object fromthe database, not create a new one so I do not have to update all the fields.
#RequestMapping("/save")
public String save(#ModelAttribute("item") BenefitType benefitType, BindingResult result)
{
...check for errors
...save, etc.
}
There are several options:
In the simpliest case when your object has only simple properties you can bind all its properties to the form fields (hidden if necessary), and get a fully bound object after submit. Complex properties also can be bound to the form fields using PropertyEditors.
You may also use session to store your object between GET and POST requests. Spring 3 faciliates this approach with #SessionAttributes annotation (from the Petclinic sample):
#Controller
#RequestMapping("/owners/*/pets/{petId}/edit")
#SessionAttributes("pet") // Specify attributes to be stored in the session
public class EditPetForm {
...
#InitBinder
public void setAllowedFields(WebDataBinder dataBinder) {
// Disallow binding of sensitive fields - user can't override
// values from the session
dataBinder.setDisallowedFields("id");
}
#RequestMapping(method = RequestMethod.GET)
public String setupForm(#PathVariable("petId") int petId, Model model) {
Pet pet = this.clinic.loadPet(petId);
model.addAttribute("pet", pet); // Put attribute into session
return "pets/form";
}
#RequestMapping(method = { RequestMethod.PUT, RequestMethod.POST })
public String processSubmit(#ModelAttribute("pet") Pet pet,
BindingResult result, SessionStatus status) {
new PetValidator().validate(pet, result);
if (result.hasErrors()) {
return "pets/form";
} else {
this.clinic.storePet(pet);
// Clean the session attribute after successful submit
status.setComplete();
return "redirect:/owners/" + pet.getOwner().getId();
}
}
}
However this approach may cause problems if several instances of the form are open simultaneously in the same session.
So, the most reliable approach for the complex cases is to create a separate object for storing form fields and merge changes from that object into persistent object manually.
So I ended up resolving this by annotating a method with a #ModelAttribute of the same name in the class. Spring builds the model first before executing the request mapping:
#ModelAttribute("item")
BenefitType getBenefitType(#RequestParam("id") String id) {
// return benefit type
}
While it is possible that your domain model is so simple that you can bind UI objects directly to data model objects, it is more likely that this is not so, in which case I would highly recommend you design a class specifically for form binding, then translate between it and domain objects in your controller.
I'm a little confused. I think you're actually talking about an update workflow?
You need two #RequestMappings, one for GET and one for POST:
#RequestMapping(value="/update/{id}", method=RequestMethod.GET)
public String getSave(ModelMap model, #PathVariable Long id)
{
model.putAttribute("item", benefitDao.findById(id));
return "view";
}
then on the POST actually update the field.
In you example above, your #ModelAttribute should already be populated with a method like the above method, and the properties be bound using something like JSTL or Spring tabglibs in conjunction with the form backing object.
You may also want to look at InitBinder depending on your use case.