I have two entities, they are: Person and Address, relation one to one. The source code about JPA/Hibernate has been removed to keep the things simple.
My problem is with Hibernate Validator.
I have the following for the Person class:
@Valid
@NotNull(message="{person.address.null}", groups={AddressCheck.class})
public Address getAddress() {
return address;
}
and for Address:
@NotNull(message="{field.null}", groups=AddressCheck.class)
public Person getPerson() {
return person;
}
Note: About JUnit, all works fine, if some dependency is null the respective error is created.
The problem is when Hibernate Validator is integrated with Spring MVC, it through the binding approach
About the view code, below the JSP fragment for the Address class
<form:form modelAttribute="person" method="post" >
...
<fieldset>
<legend><spring:message code="address.form.legend"/></legend>
<table>
<form:hidden path="address.id"/>
<tr>
<td><spring:message code="address.form.street"/></td>
<td><form:input path="address.street" size="40"/></td>
<td><form:errors path="address.street" cssClass="error"/></td>
</tr>
<tr>
<td><spring:message code="address.form.number"/></td>
<td><form:input path="address.number" size="40"/></td>
<td><form:errors path="address.number" cssClass="error"/></td>
</tr>
</table>
</fieldset>
To create the form view, to let the JSP render both objects, I have the following:
@RequestMapping(value="/register.htm", method=RequestMethod.GET)
public String createRegisterForm(Model model){
logger.info("createRegisterForm GET");
Person person = new Person();
Address address = new Address();
address.setId("5");//just to play/test
person.setAddress(address);
address.setPerson(person);
model.addAttribute("person", person);
return "jsp/person/registerForm";
}
Observe how the two setters are called.
When I do the submit the following code must be executed
@RequestMapping(value="/register.htm", method=RequestMethod.POST)
public String registerPerson( @Validated(PersonRegistrationOrdered.class) Person person,
BindingResult result,
Model model){
logger.info("registerPerson POST");
logger.info("{}", person.toString());
logger.info("{}", person.getAddress().toString());
if(result.hasErrors()){
logger.error("There are errors!!!!");
model.addAttribute("person", person);
for(ObjectError objectError : result.getAllErrors()){
logger.error("Error {}", objectError);
}
return "jsp/person/registerForm";
}
logger.info("All fine!!!!");
return "jsp/manolo";
}
Sadly the console shows an error about the Person object from the Address class is null, even when in the createRegisterForm it has been related address.setPerson(person);
My unique way to around this is disabling the validation
//@NotNull(message="{field.null}", groups=AddressCheck.class)
public Person getPerson() {
return person;
}
First Question: how I can avoid this without disabling the @NotNull
validation?
Even more, if I want protect the id for the Address class:
@InitBinder
public void initBinder(WebDataBinder binder) {
...
binder.setDisallowedFields("address.id");
}
The HTML code generated from the JSP file is
<fieldset>
<legend>Address:</legend>
<table>
<input id="address.id" name="address.id" type="hidden" value="5"/>
<tr>
<td>Street:</td>
<td><input id="address.street" name="address.street" type="text" value="" size="40"/></td>
<td></td>
</tr>
<tr>
<td>Number:</td>
<td><input id="address.number" name="address.number" type="text" value="" size="40"/></td>
<td></td>
</tr>
</table>
</fieldset>
But again when I do the submit I get
- ValidationMessages found.
- org.hibernate.validator.ValidationMessages found.
- registerPerson POST
- Person [id=5, firstName=Manuel, lastName=Jordan, alive=true, dateBirth=Wed Dec 12 00:00:00 PET 2012, dateRetirement=null, dateDeath=null, age=15, weight=34.0, salary=1500]
- Address [id=null, street=La blanquita, number=155]
- There are errors!!!!
- Error Field error in object 'person' on field 'address.id': rejected value [null]; codes [NotNull.person.address.id,NotNull.address.id,NotNull.id,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [person.address.id,address.id]; arguments []; default message [address.id]]; default message [The field must be not empty]
If I am creating a new Person and using perhaps a special class giving a potential id (5 for example address.setId("5");
in this case) for the Address's id it is going to be a problem.
Second Question: how I can turn around this problem?