Why we add #ModelAttribute("r") Reg reg in controller class? - spring

#RequestMapping(value="/addpost",method=RequestMethod.POST)
public ModelAndView addpost(HttpServletRequest request,HttpServletResponse response,#ModelAttribute("r") Reg reg)
{
int id=r.id;
System.out.println(id);
return mv;
}
In this code,there is given #ModelAttribute("r") Reg reg.Wether this modelattribute is taking values from jsp page?Can anyone explains the working of this ModelAttribute?

There are two usage of #ModelAttribute
At the Method Level
When the annotation is used at the method level it indicates the purpose of that method is to add one or more model attributes. Such methods support the same argument types as #RequestMapping methods but that cannot be mapped directly to requests.
#ModelAttribute
public void addAttributes(Model model) {
model.addAttribute("msg", "Welcome to the Netherlands!");
}
In the example, method adds an attribute named msg to all models defined in the controller class.
As a Method Argument
When used as a method argument, it indicates the argument should be retrieved from the model. When not present, it should be first instantiated and then added to the model and once present in the model, the arguments fields should be populated from all request parameters that have matching names.
In the code snippet that follows the employee model attribute is populated with data from a form submitted to the addEmployee endpoint.
#RequestMapping(value = "/addEmployee", method = RequestMethod.POST)
public String submit(#ModelAttribute("employee") Employee employee) {
// Code that uses the employee object
return "employeeView";
}
Refer : What is #ModelAttribute in Spring MVC?

Related

Spring Mvc ModelAttribute with referencing name is not working?

I want to to create different #Entity entities within the same Controller.
#RequestMapping(value="create", method=RequestMethod.GET)
public String GET(Model model) throws InstantiationException, IllegalAccessException
{
Class<?> clazz = ????; // a Random POJO is chosen, i want to use POJOs!!
Object object = clazz.newInstance();
model.addAttribute("object", object);
return "create";
}
#RequestMapping(value="create", method=RequestMethod.POST)
public #ResponseBody Object POST(#ModelAttribute(value="object") Object object)
{
System.out.println("POST! got type: " + object.getClass().getName());
return object;
}
In the Post Method I get NULL for #ModelAttribute(value="object") Object object
If I change it to #ModelAttribute(value="object") realType object it is working perfectly fine. But I don't know the type yet.
I thought the #ModelAttribute can achieve this anyway with the name "object" but apparently not. What am I missing?
There is no actual model object named object when you submit, spring constructs it based on the parameter type and will bind the properties accordingly.
You have 2 choices to make it work
Store the object in the session
Use a #ModelAttribute annotated method
If neither of these are there spring will simply look at the method argument and use reflection to construct an instance of that class. So in your case it will only be Object and after that binding will fail.
Store object in the session
#Controller
#SessionAttributes("object")
public class MyController { ... }
Make sure that when you are finished that you call the setComplete() method on a SessionStatus object.
Use a #ModelAttribute annotated method
Instead of creating and adding the object in a request handling method create a speficic method for it.
#ModelAttribute("object")
public Object formBackingObject() {
Class<?> clazz = ????; // a Random POJO is chosen, i want to use POJOs!!
Object object = clazz.newInstance();
return object;
}
This method will be called before each request handling method, so that a newly fresh object is constructed which will be used for binding.

Why didn't initbinder call on submit form?

I have a form, but when I submit it, my initbinder doesn't intercept my post request.
This is my initbinder:
#InitBinder(value="confermaDto")
protected void initBinderDto(final WebDataBinder binder, final Locale locale) {
binder.registerCustomEditor(MyClass.class, myClassEditor);
}
And this is my method to intercept the post:
#RequestMapping(value="confermaDati", method = RequestMethod.POST)
public String confermaDati(#Valid final ConfermaDatiAttrezzaturaDto confermaDto,
final BindingResult bindingResult, final Model uiModel, final HttpServletRequest httpServletRequest) {
if (bindingResult.hasErrors()) {
uiModel.addAttribute("message", "Errore Salvataggio");
uiModel.addAttribute("dto", confermaDto);
}
uiModel.asMap().clear();
return "redirect:/";
}
I think, that it should work because the value in initbinder, and the name of my model attribure are equal.
So why don't it work??
Thank you
The names of command/form attributes and/or request parameters that this init-binder method is supposed to apply to.
Default is to apply to all command/form attributes and all request parameters processed by the annotated handler class. Specifying model attribute names or request parameter names here restricts the init-binder method to those specific attributes/parameters, with different init-binder methods typically applying to different groups of attributes or parameters.
Above is from the javadoc of #InitBinder.
In your code you specify the name of a model attribute to use, namely confermaDto. However in your request handling method there is no notion of a model attribute (i.e. nothing is annotated with #ModelAttribute).
public String confermaDati(#Valid final ConfermaDatiAttrezzaturaDto confermaDto, final BindingResult bindingResult, final Model uiModel, final HttpServletRequest httpServletRequest) { ... }
You have a argument annotated with #Valid which will only trigger validation, Spring will also instantiate this object and put values from the request onto it however it is NOT designated as a model attribute. Next to your #Valid annotation add the #ModelAttribute annotation. (Or remove the name from the #InitBinder annotation so that it will always be applied).
I think, that it should work because the value in initbinder, and the name of my model attribute are equal. So why don't it work??
In short to answer this question, the method argument name is equal but there is no model attribute. Hence no triggering of the #InitBinder method.
If you do not specify a ModelAttribute value into RequestMapping annotated method You have to specify in value attribute of #Initbinder anotation the class required name with first letter NOT capitalized.

How to pass model attributes between method inside controller Spring MVC

Good day folks.
I have couple methods inside controller I want to pass model attributes between them,
First method gets data from a database:
#RequestMapping(value="/result", method=RequestMethod.GET)
public String resultHTML(#RequestParam String name, #ModelAttribute("fbosAttributes") FormBackingObjectSearch fbos,BindingResult bindingResult, Model model) throws Exception {
model.addAttribute("findAttributes", educationWebService.fetchByNam(fbos.getName()));
return "search";
And another method have to get attributes witch was created from method above:
#RequestMapping(value="/result.xls", method=RequestMethod.GET)
public String resultXLS(#ModelAttribute("findAttributes") ArrayList<FormDate> mylists, Model model) throws Exception {
model.addAttribute("findAttributesNew", mylists);
return "xlspage";
}
when I check for mylists.size() it shows/returns 0
Please help.
You can add session attributes to your controller class by this annotation:
#SessionAttributes({"findAttributes"})

I am confused about how to use #SessionAttributes

I am trying to understand architecture of Spring MVC. However, I am completely confused by behavior of #SessionAttributes.
Please look at SampleController below , it is handling post method by SuperForm class. In fact, just field of SuperForm class is only binding as I expected.
However, After I put #SessionAttributes in Controller, handling method is binding as SubAForm. Can anybody explain me what happened in this binding.
-------------------------------------------------------
#Controller
#SessionAttributes("form")
#RequestMapping(value = "/sample")
public class SampleController {
#RequestMapping(method = RequestMethod.GET)
public String getCreateForm(Model model) {
model.addAttribute("form", new SubAForm());
return "sample/input";
}
#RequestMapping(method = RequestMethod.POST)
public String register(#ModelAttribute("form") SuperForm form, Model model) {
return "sample/input";
}
}
-------------------------------------------------------
public class SuperForm {
private Long superId;
public Long getSuperId() {
return superId;
}
public void setSuperId(Long superId) {
this.superId = superId;
}
}
-------------------------------------------------------
public class SubAForm extends SuperForm {
private Long subAId;
public Long getSubAId() {
return subAId;
}
public void setSubAId(Long subAId) {
this.subAId = subAId;
}
}
-------------------------------------------------------
<form:form modelAttribute="form" method="post">
<fieldset>
<legend>SUPER FIELD</legend>
<p>
SUPER ID:<form:input path="superId" />
</p>
</fieldset>
<fieldset>
<legend>SUB A FIELD</legend>
<p>
SUB A ID:<form:input path="subAId" />
</p>
</fieldset>
<p>
<input type="submit" value="register" />
</p>
</form:form>
When processing POST request, Spring does the following:
Without #SessionAttributes: Spring instantiates a new instance of SuperForm (type is inferred from the signature of register()), populates its properties by values from the form fields and passes it to the register() method.
With #SessionAttributes: Spring obtains an instance of model attribute from the session (where it was placed when processing GET due to presence of #SessionAttributes), updates its properties by values from the from fields and passes it to the register() method.
That is, with #SessionAttributes , register() gets the same instance of the model attribute object that was placed into the Model by getCreateForm().
Adding on to what #axtavt said: Suppose, in getCreateForm you are putting some values for a drop-down (list or map), or you are putting some values in form that you want in register method but you don't want them to show in form (not even in hidden fields). Now suppose that an error occurred in register method and you need to show the form again. To populate drop down values and other values that you would need in next post, you would have to repopulate them in form. The #SessionAttribute helps here as #axtavt very well described above.
#Controller
#SessionAttributes("test")
public class Controller{
Customer customer;
public Controller() {
super();
customer = new Customer();
}
#ModelAttribute("test")
public Customer getCustomer() {
customer.setName("Savac");
return customer;
}
#RequestMapping({"/index"})
public ModelAndView showMainPage (#ModelAttribute("test") Customer customer, ModelMap model, method = RequestMethod.GET) {
//in the view you set the name
return new ModelAndView("index");
}
#RequestMapping(value = "customer/{customerID}", method = RequestMethod.GET)
public ModelAndView viewAdvice(#PathVariable("customerID") int customerID, #ModelAttribute("test") Customer customer, ModelMap model) {
customer.setName("AnotherName");
model.addAttribute("test", customer);
return new ModelAndView("customer");
}
}
According to Spring reference documentation #ModelAttribute annotated method argument is resolved as follows:
Retrieve from model object if it is present (normally added via #ModelAttribute annotated methods)
Retrieve from HTTP session by using #SessionAttributes.
Create using URI path variable that matches the #ModelAttribute name through a converter
Create using default constructor and add it to Model.
A handler class can be annotated with #SessionAttributes with a list of names as its arguments. This is to instruct Spring to persist (in session) those data items present in the model data which match the names specified in #SessionAttributes annotation.
Thus in the SampleController, the post method's #ModelAttribute argument is resolved with #SessionAttributes field due to the resolution method mentioned above.

Spring MVC 3.0: How do I bind to a persistent object

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.

Resources