Hibernate JSR303 验证和错误生成的 propertyPath

发布于 2024-11-02 06:44:39 字数 3819 浏览 2 评论 0原文

我正在尝试使用 Spring MVC 设置表单的 JSR-303 验证。我已经正确配置了所有内容(或者至少我认为我做到了),并且验证大部分工作正常。但是,如果我有一个命令对象,其中包含要验证的对象集合,并且我使用 @Valid 注释该集合,则 Hibernate JSR-303 提供程序不会提供正确的 propertyPath。 ContraintViolation 对象内的 propertyPath 应像 list[0].barlist[1].bar 一样填充,但 Hibernate 验证器只是提供 list[ ].barlist[].bar。当 Spring 的 SpringValidatorAdaptor.validate() 方法尝试添加字段级错误时,这会导致 NumberFormatException(因为它内部期望这些括号内存在数字)。

使用 spring-context-3.0.5 和 hibernate-validator-4.1.0.Final (我也尝试了 4.0.2.GA 和 4.2.0.Beta2 并得到了相同的结果),我编写了一个小型单元测试来说明问题:

package com.foo;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Valid;
import javax.validation.Validation;
import javax.validation.Validator;

import org.hibernate.validator.constraints.NotEmpty;
import org.junit.Test;
import org.springframework.util.AutoPopulatingList;

public class ValidatorTest {

    class Person {
        @NotEmpty
        private String name;

        @NotEmpty
        @Valid
        private Collection<Foo> list = new AutoPopulatingList<Foo>(Foo.class);

        public void setName(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public Collection<Foo> getList() {
            return list;
        }

        public void setList(Collection<Foo> foos) {
            this.list = foos;
        }
    }

    class Foo {
        @NotEmpty
        private String bar;

        public void setBar(String bar) {
            this.bar = bar;
        }

        public String getBar() {
            return bar;
        }
    }

    @Test
    public void testValidator() throws Exception {
        Foo foo0 = new Foo();
        foo0.setBar("");

        Foo foo1 = new Foo();
        foo1.setBar("");

        Collection<Foo> list = new ArrayList<ValidatorTest.Foo>();
        list.add(foo0);
        list.add(foo1);

        Person person = new Person();
        person.setName("Test Person");
        person.setList(list);

        Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
        Set<ConstraintViolation<Person>> violations = validator.validate(person);

        for (ConstraintViolation<Person> constraintViolation : violations) {
            System.out.println(constraintViolation);
        }
    }
}

上述测试产生以下输出:

ConstraintViolationImpl{interpolatedMessage='may not be empty', propertyPath=list[].bar, rootBeanClass=class com.foo.ValidatorTest$Person, messageTemplate='{org.hibernate.validator.constraints.NotEmpty.message}'}
ConstraintViolationImpl{interpolatedMessage='may not be empty', propertyPath=list[].bar, rootBeanClass=class com.foo.ValidatorTest$Person, messageTemplate='{org.hibernate.validator.constraints.NotEmpty.message}'}

它产生的错误是正确的;但是, propertyPath 不是(至少根据我的理解)。

现在,如果我将 Hibernate 的 JSR-303 实现替换为 Apache 的 (org.apache.bval.bundle-0.2-incubating--使用指出的依赖项 此处),我得到了我期望的输出。这是完全相同的测试,但使用 Apache 的 JSR-303 注释而不是 Hibernate 的注释。请注意 propertyPath 字段中现在存在的索引:

ConstraintViolationImpl{rootBean=com.foo.ValidatorTest$Person@5f989f84, propertyPath='list[0].bar', message='may not be empty', leafBean=com.foo.ValidatorTest$Foo@4393722c, value=}
ConstraintViolationImpl{rootBean=com.foo.ValidatorTest$Person@5f989f84, propertyPath='list[1].bar', message='may not be empty', leafBean=com.foo.ValidatorTest$Foo@528acf6e, value=}

出于各种原因,我可能坚持使用 Hibernate 的 JSR-303 实现。我正在做的事情是否导致 Hibernate 验证器不填充这些索引?我试图尽可能避免在 Spring 控制器中进行手动错误处理。

感谢您提供的任何帮助!

I'm trying to set up JSR-303 validation of forms using Spring MVC. I had everything configured correctly (or at least I think I do), and validations are working mostly correctly. However, if I have a command object that contains a Collection of objects that I want validated, and I annotate that Collection with @Valid, the Hibernate JSR-303 provider is not providing the correct propertyPath. The propertyPath inside the ContraintViolation object should be populated like list[0].bar and list[1].bar, but the Hibernate validator is simply providing list[].bar and list[].bar. This causes a NumberFormatException when Spring's SpringValidatorAdaptor.validate() method tries to add field level errors (since it internally expects a number to exist within those brackets).

Using spring-context-3.0.5 and hibernate-validator-4.1.0.Final (I also tried 4.0.2.GA and 4.2.0.Beta2 and got the same results), I wrote a small unit test to illustrate the issue:

package com.foo;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Valid;
import javax.validation.Validation;
import javax.validation.Validator;

import org.hibernate.validator.constraints.NotEmpty;
import org.junit.Test;
import org.springframework.util.AutoPopulatingList;

public class ValidatorTest {

    class Person {
        @NotEmpty
        private String name;

        @NotEmpty
        @Valid
        private Collection<Foo> list = new AutoPopulatingList<Foo>(Foo.class);

        public void setName(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public Collection<Foo> getList() {
            return list;
        }

        public void setList(Collection<Foo> foos) {
            this.list = foos;
        }
    }

    class Foo {
        @NotEmpty
        private String bar;

        public void setBar(String bar) {
            this.bar = bar;
        }

        public String getBar() {
            return bar;
        }
    }

    @Test
    public void testValidator() throws Exception {
        Foo foo0 = new Foo();
        foo0.setBar("");

        Foo foo1 = new Foo();
        foo1.setBar("");

        Collection<Foo> list = new ArrayList<ValidatorTest.Foo>();
        list.add(foo0);
        list.add(foo1);

        Person person = new Person();
        person.setName("Test Person");
        person.setList(list);

        Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
        Set<ConstraintViolation<Person>> violations = validator.validate(person);

        for (ConstraintViolation<Person> constraintViolation : violations) {
            System.out.println(constraintViolation);
        }
    }
}

The above test produces the following output:

ConstraintViolationImpl{interpolatedMessage='may not be empty', propertyPath=list[].bar, rootBeanClass=class com.foo.ValidatorTest$Person, messageTemplate='{org.hibernate.validator.constraints.NotEmpty.message}'}
ConstraintViolationImpl{interpolatedMessage='may not be empty', propertyPath=list[].bar, rootBeanClass=class com.foo.ValidatorTest$Person, messageTemplate='{org.hibernate.validator.constraints.NotEmpty.message}'}

The errors that it produces are correct; however, the propertyPath isn't (at least from what I understand).

Now, if I replace Hibernate's JSR-303 implementation with Apache's (org.apache.bval.bundle-0.2-incubating--using the dependencies noted here), I get output that I expect. This is the exact same test, but using Apache's JSR-303 annotations instead of Hibernate's. Note the indexes that now exist in the propertyPath field:

ConstraintViolationImpl{rootBean=com.foo.ValidatorTest$Person@5f989f84, propertyPath='list[0].bar', message='may not be empty', leafBean=com.foo.ValidatorTest$Foo@4393722c, value=}
ConstraintViolationImpl{rootBean=com.foo.ValidatorTest$Person@5f989f84, propertyPath='list[1].bar', message='may not be empty', leafBean=com.foo.ValidatorTest$Foo@528acf6e, value=}

For various reasons, I'm probably stuck with using Hibernate's JSR-303 implementation. Is there something that I'm doing that is causing the Hibernate validator not to populate those indexes? I'm trying to keep from doing manual error handling in my Spring controllers as much as possible.

Thanks for any help that you're able to provide!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

月下伊人醉 2024-11-09 06:44:39

我在 Hibernate 论坛 上提出了这个问题好吧,它在那里得到了答案。我想在这里分享答案,以防其他人遇到这个问题。

只需将集合更改为列表即可解决问题。这是更新的单元测试:

package com.foo;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Valid;
import javax.validation.Validation;
import javax.validation.Validator;

import org.hibernate.validator.constraints.NotEmpty;
import org.junit.Test;
import org.springframework.util.AutoPopulatingList;

public class ValidatorTest {

    class Person {
        @NotEmpty
        private String name;

        @NotEmpty
        @Valid
        private List<Foo> list = new AutoPopulatingList<Foo>(Foo.class);

        public void setName(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public List<Foo> getList() {
            return list;
        }

        public void setList(List<Foo> foos) {
            this.list = foos;
        }
    }

    class Foo {
        @NotEmpty
        private String bar;

        public void setBar(String bar) {
            this.bar = bar;
        }

        public String getBar() {
            return bar;
        }
    }

    @Test
    public void testValidator() throws Exception {
        Foo foo0 = new Foo();
        foo0.setBar("");

        Foo foo1 = new Foo();
        foo1.setBar("");

        List<Foo> list = new ArrayList<ValidatorTest.Foo>();
        list.add(foo0);
        list.add(foo1);

        Person person = new Person();
        person.setName("Test Person");
        person.setList(list);

        Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
        Set<ConstraintViolation<Person>> violations = validator.validate(person);

        for (ConstraintViolation<Person> constraintViolation : violations) {
            System.out.println(constraintViolation);
        }
    }
}

以及上述测试的输出(现在包括索引):

ConstraintViolationImpl{interpolatedMessage='may not be empty', propertyPath=list[1].bar, rootBeanClass=class com.foo.ValidatorTest$Person, messageTemplate='{org.hibernate.validator.constraints.NotEmpty.message}'}
ConstraintViolationImpl{interpolatedMessage='may not be empty', propertyPath=list[0].bar, rootBeanClass=class com.foo.ValidatorTest$Person, messageTemplate='{org.hibernate.validator.constraints.NotEmpty.message}'}

I asked this question on the Hibernate forums as well, and it got answered there. I wanted to share the answer here in case anyone else encounters this issue.

Simply changing the Collection to a List solves the problem. Here's the updated unit test:

package com.foo;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Valid;
import javax.validation.Validation;
import javax.validation.Validator;

import org.hibernate.validator.constraints.NotEmpty;
import org.junit.Test;
import org.springframework.util.AutoPopulatingList;

public class ValidatorTest {

    class Person {
        @NotEmpty
        private String name;

        @NotEmpty
        @Valid
        private List<Foo> list = new AutoPopulatingList<Foo>(Foo.class);

        public void setName(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public List<Foo> getList() {
            return list;
        }

        public void setList(List<Foo> foos) {
            this.list = foos;
        }
    }

    class Foo {
        @NotEmpty
        private String bar;

        public void setBar(String bar) {
            this.bar = bar;
        }

        public String getBar() {
            return bar;
        }
    }

    @Test
    public void testValidator() throws Exception {
        Foo foo0 = new Foo();
        foo0.setBar("");

        Foo foo1 = new Foo();
        foo1.setBar("");

        List<Foo> list = new ArrayList<ValidatorTest.Foo>();
        list.add(foo0);
        list.add(foo1);

        Person person = new Person();
        person.setName("Test Person");
        person.setList(list);

        Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
        Set<ConstraintViolation<Person>> violations = validator.validate(person);

        for (ConstraintViolation<Person> constraintViolation : violations) {
            System.out.println(constraintViolation);
        }
    }
}

And the output from the above test (which now includes indexes):

ConstraintViolationImpl{interpolatedMessage='may not be empty', propertyPath=list[1].bar, rootBeanClass=class com.foo.ValidatorTest$Person, messageTemplate='{org.hibernate.validator.constraints.NotEmpty.message}'}
ConstraintViolationImpl{interpolatedMessage='may not be empty', propertyPath=list[0].bar, rootBeanClass=class com.foo.ValidatorTest$Person, messageTemplate='{org.hibernate.validator.constraints.NotEmpty.message}'}
乖不如嘢 2024-11-09 06:44:39

如果您使用数据尚未保留到数据库中的对象调用 getResultList() 方法,则会出现相同的错误。

The same error appear if you call method getResultList() with object which data is not still persisted into database.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文