@ModelAttribute annotation, when to use it?
Asked Answered
E

3

41

Lets say we have an entity Person, a controller PersonController and an edit.jsp page (creating a new or editing an existing person)

Controller

@RequestMapping(value = "/edit", method = RequestMethod.POST)
public String editPerson(@RequestParam("fname") String fname, Model model) {
    if(fname == null || fname.length() == 0){
        model.addAttribute("personToEditOrCreate", new Person());
    }
    else{
        Person p = personService.getPersonByFirstName(fname);
        model.addAttribute("personToEditOrCreate", p);
    }

    return "persons/edit";
}

@RequestMapping(value = "/save", method = RequestMethod.POST)
public String savePerson(Person person, BindingResult result) {

    personService.savePerson(person);
    return "redirect:/home";
}

edit.jsp

<form:form method="post" modelAttribute="personToEditOrCreate" action="save">
    <form:hidden path="id"/> 
    <table>
        <tr>
            <td><form:label path="firstName">First Name</form:label></td>
            <td><form:input path="firstName" /></td>
        </tr>
        <tr>
            <td><form:label path="lastName">Last Name</form:label></td>
            <td><form:input path="lastName" /></td>
        </tr>
        <tr>
            <td><form:label path="money">Money</form:label></td>
            <td><form:input path="money" /></td>
        </tr>
        <tr>
            <td colspan="2">
                <input type="submit" value="Add/Edit Person"/>
            </td>
        </tr>
    </table> 

</form:form>

Im trying the code above (without using the @ModelAttribute annotation in the savePerson method, and it works correct. Why and when do i need to add the annotation to the person object:

@RequestMapping(value = "/save", method = RequestMethod.POST)
public String savePerson(@ModelAttribute("personToEditOrCreate") Person person, BindingResult result) {

    personService.savePerson(person);
    return "redirect:/home";
}
Escarpment answered 31/12, 2011 at 12:41 Comment(0)
G
72

You don't need @ModelAttribute (parameter) just to use a Bean as a parameter

For example, these handler methods work fine with these requests:

@RequestMapping("/a")
void pathA(SomeBean someBean) {
  assertEquals("neil", someBean.getName());
}

GET /a?name=neil

@RequestMapping(value="/a", method=RequestMethod.POST)
void pathAPost(SomeBean someBean) {
  assertEquals("neil", someBean.getName());
}

POST /a
name=neil

Use @ModelAttribute (method) to load default data into your model on every request - for example from a database, especially when using @SessionAttributes. This can be done in a Controller or in a ControllerAdvice:

@Controller
@RequestMapping("/foos")
public class FooController {

  @ModelAttribute("foo")
  String getFoo() {
    return "bar";  // set modelMap["foo"] = "bar" on every request
  }

}

Any JSP forwarded to by FooController:

${foo} //=bar

or

@ControllerAdvice
public class MyGlobalData {

  @ModelAttribute("foo")
  String getFoo() {
    return "bar";  // set modelMap["foo"] = "bar" on every request
  }

}

Any JSP:

${foo} //=bar

Use @ModelAttribute (parameter) if you want to use the result of @ModelAttribute (method) as a default:

@ModelAttribute("attrib1")
SomeBean getSomeBean() {
  return new SomeBean("neil");  // set modelMap["attrib1"] = SomeBean("neil") on every request
}

@RequestMapping("/a")
void pathA(@ModelAttribute("attrib1") SomeBean someBean) {
  assertEquals("neil", someBean.getName());
}

GET /a

Use @ModelAttribute (parameter) to get an object stored in a flash attribute:

@RequestMapping("/a")
String pathA(RedirectAttributes redirectAttributes) {
  redirectAttributes.addFlashAttribute("attrib1", new SomeBean("from flash"));
  return "redirect:/b";
}

@RequestMapping("/b")
void pathB(@ModelAttribute("attrib1") SomeBean someBean) {
  assertEquals("from flash", someBean.getName());
}

GET /a

Use @ModelAttribute (parameter) to get an object stored by @SessionAttributes

@Controller
@SessionAttributes("attrib1")
public class Controller1 {

    @RequestMapping("/a")
    void pathA(Model model) {
        model.addAttribute("attrib1", new SomeBean("neil")); //this ends up in session due to @SessionAttributes on controller
    }

    @RequestMapping("/b")
    void pathB(@ModelAttribute("attrib1") SomeBean someBean) {
        assertEquals("neil", someBean.getName());
    }
}

GET /a
GET /b

Guillemette answered 13/11, 2014 at 19:34 Comment(6)
How to use @ModelAttribute in PUT methodBadenpowell
Just Wow. So usefulBasque
How would you set default values for @ModelAttribute when using it as a parameter? Ex: (@RequestParam(value = "studentName", defaultValue="Alexa")Shuttering
the part that ModelAttribute being used to get a model passes by RedirectAttribute is very enlightening. thanksFirm
I frankly think, that the @ModelAttribute is one of the most vaguely named, unintuitively described and poorly documented annotation. It brings so much ambiguity, confusion and head-scratching, to so many people..Stamata
@GiorgiTsiklauri I agree. They should at least split it to @SetModelAttribute and @GetModelAttribute or similarGuillemette
D
3

Your question appears to be answered already:

What is @ModelAttribute in Spring MVC?

To summarize the answer and blog post: when you want your form backing object (instance of Person) to be persisted across requests.

Otherwise, without the annotation, the request mapped method will assume Person is a new object and in no way linked to your form backing object. The blog post that poster references is really awesome by the way, definitely a must-read.

Douceur answered 31/12, 2011 at 13:7 Comment(1)
...but values in Model are not persisted across requests unless you are using RedirectAttributesModelMap? init?Morra
C
0

An @ModelAttribute on a method argument indicates the argument will be retrieved from the model.If not present in the model, the argument will be instantiated first and then added to the model.

Carrew answered 26/7, 2016 at 10:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.