1

我有两个实体,它们是:Person 和 Address,一对一的关系。关于 JPA/Hibernate 的源代码已被删除以保持简单。

我的问题是休眠验证器。

对于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,一切正常,如果某些依赖项为空,则会创建相应的错误。

问题是当 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 并使用一个特殊的类,address.setId("5");为地址的 id 提供一个潜在的 id(例如在本例中为 5),这将是一个问题。

第二个问题:我怎样才能扭转这个问题?

4

1 回答 1

1

问题是您正在使用GET. 这意味着一旦页面被渲染,模型对象就消失了。当POST出现新的空白时Person,由于自动路径扩展也会创建,Address但这不会正确应用关系。

有两种方法可以解决此问题。

  1. 创建一个注解的方法,@ModelAttribute该方法构造了用于绑定所需的模型。
  2. 添加@SessionAttributes以在请求之间存储Person在会话中。

使任一选项起作用的另一件事是,@Validated您需要添加@ModelAttribute到您的方法Person方法参数中。

方法与@ModelAttribute

首先添加创建模型对象的方法。为此,创建一个方法并对其进行注释,@ModelAttribute并可选择为其命名模型对象person。(这也是默认设置,因为这是使用带有小写首字母的类名)。

@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 方法。首先,对于这两种方法,您都可以删除Modelas 方法参数,因为不再需要它。已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以便清除会话中过时的模型对象。

于 2014-09-10T13:10:18.230 回答