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!
@ModelAttributebecause it really isn't that. The problem is that databinding just binds requestparameters to objects based on their name. So if there is a parameterhouseNoand there is asetHouseNoon any model object it will be bound.