0

I'm having a problem with @ModelAttribute usage.

Having a simple Spring (3.2.11) mvc application with such context:

<mvc:annotation-driven />
<context:component-scan base-package="spring.test" />
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/"></property>
    <property name="suffix" value=".jsp"></property>
</bean> 

Having 2 DTOs: PageDTO and SessionDTO, inside these two DTOs are completely the same, containing just one property - Address address and get/set methods.

package spring.test.model;

public class PageDTO implements java.io.Serializable {
    private Address address;
    // getters / setters omitted
}


package spring.test.model;

public class SessionDTO implements java.io.Serializable {
    private Address address;
    // getters / setters omitted
}

package spring.test.model;

public class Address implements java.io.Serializable {
    private String street;
    private String houseNo;
    private String city;
    private String zip;    
    // getters/setters omitted
}

Having an addressForm.jsp with one form:

<c:url var="actionURL" value="/processForm"/>
<form:form method="POST" modelAttribute="pageDto" action="${actionURL}">
    <form:label path="address.street">Street: </form:label>
    <form:input path="address.street"/>
    <form:label path="address.houseNo">House No: </form:label>
    <form:input path="address.houseNo"/>
    <form:label path="address.city">City: </form:label>
    <form:input path="address.city"/>
    <form:label path="address.zip">ZIP: </form:label>
    <form:input path="address.zip"/>
    <input type="submit" name="searchAddress" value="Submit" />
</form:form>

Having a controller:

package spring.test.controller;

// imports omitted

@SessionAttributes("sessionDto")
@Controller
public class FormController {

    @InitBinder
    protected void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
    }

    @ModelAttribute("pageDto")
    public PageDTO initPageDTO() {
        return new PageDTO();
    }  

    @ModelAttribute("sessionDto")
    public SessionDTO initSessionDTO() {
        return new SessionDTO();
    }

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public ModelAndView renderHome(@ModelAttribute("pageDto") PageDTO pageDto, 
            @ModelAttribute("sessionDto") SessionDTO sessionDto) {       
        return new ModelAndView("addressForm", "pageDto", pageDto);
    }

    @RequestMapping(value = "/processForm", method = RequestMethod.POST)
    public ModelAndView processForm(@ModelAttribute("pageDto") PageDTO pageDto, 
            @ModelAttribute("sessionDto") SessionDTO sessionDto) {
        if(pageDto != null && pageDto.getAddress() != null
                && sessionDto != null && sessionDto.getAddress() != null) {
            System.out.println("pageDto.getAddress().getHouseNo(): " + pageDto.getAddress().getHouseNo());
            System.out.println("sessionDto.getAddress().getHouseNo(): " + sessionDto.getAddress().getHouseNo());
        }
        return new ModelAndView("addressForm", "pageDto", pageDto);
    }

}

And here comes the problem. When I submit the address form, both of the DTOs are filled in with the data from the form. I would expect only the model attribute with the "pageDto" name mentioned in the form tag in the jsp to be filled in.

Log entry from the form-submit event:

[10/24/14 11:23:09:382 CEST] 0000002b SystemOut     O pageDto.getAddress().getHouseNo(): 41
[10/24/14 11:23:09:382 CEST] 0000002b SystemOut     O sessionDto.getAddress().getHouseNo(): 41

When I change the processForm method in the controller like this:

@RequestMapping(value = "/processForm", method = RequestMethod.POST)
public ModelAndView processForm(@ModelAttribute("pageDto") PageDTO pageDto,
        HttpSession httpSession) {
    SessionDTO sessionDto = (SessionDTO) httpSession.getAttribute("sessionDto");
    if(pageDto != null && pageDto.getAddress() != null
            && sessionDto != null && sessionDto.getAddress() != null) {
        System.out.println("pageDto.getAddress().getHouseNo(): " + pageDto.getAddress().getHouseNo());
        System.out.println("sessionDto.getAddress().getHouseNo(): " + sessionDto.getAddress().getHouseNo());
    }
    return new ModelAndView("addressForm", "pageDto", pageDto);
}

...it works as desired - sessionDto is NOT filled in with data from the form, so no entry appears in the log (sessionDto.getAddress() == null).

Motivation to have 2 DTOs, one stored in the session:

  • not all properties of the model are displayed on the form, so I would have to use input type=hidden not to loose data
  • I need to be able to find out what properties are changed by user when submitting the form

Any ideas appreciated!

3
  • Store it in the session yourself and retrieve it. Don't use it as a @ModelAttribute because it really isn't that. The problem is that databinding just binds requestparameters to objects based on their name. So if there is a parameter houseNo and there is a setHouseNo on any model object it will be bound. Commented Oct 24, 2014 at 10:16
  • So what is the meaning of the name in @ModelAttribute("name") if not to distinguish the model objects? Commented Oct 24, 2014 at 10:39
  • To retrieve the correct object from the model but all model objects are considered for binding. Commented Oct 24, 2014 at 10:41

1 Answer 1

1

At submit time, the name of the ModelAttribute has mainly cosmetic effect. The request parameters consist in a hash where keys are the paths of the <form:input> elements sent by previous response (*), and the values comes from what user typed in the form. But the name of the modelAttribute is not in the request and Spring has no way to guess it.

It takes all the @ModelAttribute parameters of the method and tries to set the attributes corresponding the to keys of the request parameters, and when it's done, puts them in the Model with the name you supplied.

So the name is only used to prepare the response and not to parse the request.

(*) if the request comes from submitting the form, but it could be produced by any other way - simply Spring MVC expects request parameters following that convention

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.