У меня есть две сущности, это: Человек и Адрес, отношение один к одному. Исходный код JPA/Hibernate был удален для простоты.
Моя проблема связана с Hibernate Validator.
У меня есть следующее для класса Person:
@Valid
@NotNull(message="{person.address.null}", groups={AddressCheck.class})
public Address getAddress() {
return address;
}
и для Адрес:
@NotNull(message="{field.null}", groups=AddressCheck.class)
public Person getPerson() {
return person;
}
Примечание. Что касается JUnit, все работает нормально, если какая-то зависимость имеет значение null, создается соответствующая ошибка.
Проблема заключается в том, что когда Hibernate Validator интегрирован с Spring MVC, он использует подход binding
О коде представления ниже фрагмента JSP для класса Address
<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>
Чтобы создать представление формы, чтобы JSP отображал оба объекта, у меня есть следующее:
@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";
}
Обратите внимание, как вызываются два сеттера.
Когда я делаю отправку, должен быть выполнен следующий код
@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";
}
К сожалению, консоль показывает ошибку о том, что объект Person из класса Address имеет значение null, даже если в createRegisterForm он был связан address.setPerson(person);
Мой уникальный способ обойти это - отключить проверку
//@NotNull(message="{field.null}", groups=AddressCheck.class)
public Person getPerson() {
return person;
}
Первый вопрос: как этого избежать, не отключая проверку @NotNull
?
Более того, если я хочу защитить id для класса Address:
@InitBinder
public void initBinder(WebDataBinder binder) {
...
binder.setDisallowedFields("address.id");
}
Код HTML, сгенерированный из файла JSP,
<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>
Но опять же, когда я делаю отправку, я получаю
- 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]
Если я создаю нового человека и использую, возможно, специальный класс, дающий потенциальный идентификатор (5, например, address.setId("5");
в данном случае) для идентификатора адреса, это будет проблемой.
Второй вопрос: как решить эту проблему?