我有两个实体,它们是:人和地址,一对一的关系。关于JPA / Hibernate的源代码已被删除,以保持简单。
我的问题在于Hibernate Validator。
我对人类有以下内容:
@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 集成时,它通过绑定方法
关于视图代码,在Address类的JSP片段下面
<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";
}
观察两个setter的调用方式。
当我提交时,必须执行以下代码
@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";
}
可悲的是,控制台显示有关Address类中Person对象的错误为null,即使在 createRegisterForm 中它已被关联address.setPerson(person);
我解决此问题的独特方法是禁用验证
//@NotNull(message="{field.null}", groups=AddressCheck.class)
public Person getPerson() {
return person;
}
第一个问题:如何在不停用@NotNull
验证的情况下避免这种情况?
更重要的是,如果我想保护Address类的 id :
@InitBinder
public void initBinder(WebDataBinder binder) {
...
binder.setDisallowedFields("address.id");
}
从JSP文件生成的HTML代码是
<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]
如果我正在创建一个新的Person并且使用一个特殊的类来提供一个潜在的id(例如在这种情况下为5 {例如address.setId("5");
),那么对于Address的id来说,这将是一个问题。< / p>
第二个问题:我怎么能扭转这个问题?
答案 0 :(得分:1)
问题是您在使用GET
的方法中创建模型。这意味着只要页面被渲染,模型对象就会消失。当POST
进入新的空Person
时,由于自动路径扩展也会Address
,但这不会正确应用关系。
有两种方法可以解决这个问题。
@ModelAttribute
注释的方法,该方法构造了用于绑定的模型。@SessionAttributes
以在请求之间的会话中存储Person
。 要使这两个选项都有效,另一件事就是在@Validated
旁边,您需要将@ModelAttribute
添加到方法Person
方法参数中。
@ModelAttribute
首先添加创建模型对象的方法。为此,创建一个方法并使用@ModelAttribute
对其进行注释,并可选择为其指定模型对象person
的名称。 (这也是默认值,因为使用带有小写首字母的classname)。
@ModelAttribute("person")
public Person prepareModel() {
Person person = new Person();
Address address = new Address();
address.setId("5");//just to play/test
person.setAddress(address);
address.setPerson(person);
return person;
}
接下来修改您的GET和POST方法。首先,对于这两种方法,您可以删除Model
作为方法参数,因为不再需要它。 Person
已添加到模型中。
@RequestMapping(value="/register.htm", method=RequestMethod.GET)
public String createRegisterForm(){
logger.info("createRegisterForm GET");
return "jsp/person/registerForm";
}
对于POST添加@ModelAttribute
,您不需要
@RequestMapping(value="/register.htm", method=RequestMethod.POST)
public String registerPerson( @Validated(PersonRegistrationOrdered.class) @ModelAttribute Person person,
BindingResult result){
logger.info("registerPerson POST");
logger.info("{}", person.toString());
logger.info("{}", person.getAddress().toString());
if(result.hasErrors()){
logger.error("There are errors!!!!");
for(ObjectError objectError : result.getAllErrors()){
logger.error("Error {}", objectError);
}
return "jsp/person/registerForm";
}
logger.info("All fine!!!!");
return "jsp/manolo";
}
@SessionAttributes
使用@SessionAttributes
,您可以在HttpSession
中识别要临时存储的对象。将此批注添加到控制器类中,并在POST方法中添加SessionStatus
作为参数,并删除已填充的Model
。
@Controller
@SessionAttributes(Person.class)
public class RegisterController {
@RequestMapping(value="/register.htm", method=RequestMethod.POST)
public String registerPerson( @Validated(PersonRegistrationOrdered.class) @ModelAttribute Person person,
BindingResult result, SessionStatus status){
logger.info("registerPerson POST");
logger.info("{}", person.toString());
logger.info("{}", person.getAddress().toString());
if(result.hasErrors()){
logger.error("There are errors!!!!");
for(ObjectError objectError : result.getAllErrors()){
logger.error("Error {}", objectError);
}
return "jsp/person/registerForm";
}
logger.info("All fine!!!!");
status.setComplete();
return "jsp/manolo";
}
}
您需要调用setComplete()
上的SessionStatus
方法,以便会话清除过时的模型对象。