2011-04-20 66 views
4

我試圖設置使用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,得到了相同的結果),我寫了一個小單元測試爲了說明該問題:Hibernate JSR303驗證和錯誤生成屬性路徑

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 - 使用標記爲here的依賴關係),我得到了我期望的輸出結果。這是完全相同的測試,但使用Apache的JSR-303註釋而不是Hibernate's。請注意現在存在於propertyPath字段中的索引:

ConstraintViolationImpl{[email protected], propertyPath='list[0].bar', message='may not be empty', [email protected], value=} 
ConstraintViolationImpl{[email protected], propertyPath='list[1].bar', message='may not be empty', [email protected], value=} 

由於各種原因,我可能堅持使用Hibernate的JSR-303實現。有沒有我正在做的事情導致Hibernate驗證器不填充這些索引?我試圖儘可能避免在我的Spring控制器中進行手動錯誤處理。

感謝您提供任何幫助!

回答

5

我在Hibernate forums上問了這個問題,它在那裏得到了答案。我想在這裏分享答案,以防其他人遇到此問題。

只需將Collection更改爲List即可解決問題。下面是更新單元測試:

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}'}