Hibernate validation 注解 springmvc 驗證 分組


 

SpringMVC驗證框架Validation特殊用法

 

1. 分組

有的時候,我們對一個實體類需要有多中驗證方式,在不同的情況下使用不同驗證方式,比如說對於一個實體類來的id來說,保存的時候是不需要的,對於更新時是必須的,可以如下配置:

[java]  view plain  copy
 
  1. public class UserModel {  
  2.   
  3.     @NotNull(message = "{id.empty}", groups = { First.class })  
  4.     private int id;  
  5.   
  6.     @NotNull(message = "{username.empty}", groups = { First.class, Second.class })  
  7.     private String username;  
  8.   
  9.     @NotNull(message = "{content.empty}", groups = { First.class, Second.class })  
  10.     private String content;  
  11.   
  12.     public int getId() {  
  13.         return id;  
  14.     }  
  15.   
  16.     public void setId(int id) {  
  17.         this.id = id;  
  18.     }  
  19.   
  20.     public String getUsername() {  
  21.         return username;  
  22.     }  
  23.   
  24.     public void setUsername(String username) {  
  25.         this.username = username;  
  26.     }  
  27.   
  28.     public String getContent() {  
  29.         return content;  
  30.     }  
  31.   
  32.     public void setContent(String content) {  
  33.         this.content = content;  
  34.     }  
  35. }  
  36. public interface First {  
  37. }  
  38.   
  39. public interface Second {  
  40. }  

通過 groups 對驗證進行分組

在controler中的代碼如下:

[java]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. @RequestMapping(value = "/save.action", method = RequestMethod.POST)  
  2. public String save(@Validated( { Second.class }) UserModel userModel, BindingResult result) {  
  3.     if (result.hasErrors()) {  
  4.         return "validate/error";  
  5.     }  
  6.     return "redirect:/success";  
  7. }  
  8.   
  9. @RequestMapping(value = "/update.action", method = RequestMethod.POST)  
  10. public String update(@Validated( { First.class, Second.class }) UserModel user, BindingResult result) {  
  11.     if (result.hasErrors()) {  
  12.         return "validate/error";  
  13.     }  
  14.     return "redirect:/success";  
  15. }  

2. 組序列

默認情況下,不同組別的約束驗證是無序的,然而在某些情況下,約束驗證的順序卻很重要,如下面兩個例子:(1)第二個組中的約束驗證依賴於一個穩定狀態來運行,而這個穩定狀態是由第一個組來進行驗證的。(2)某個組的驗證比較耗時,CPU 和內存的使用率相對比較大,最優的選擇是將其放在最后進行驗證。因此,在進行組驗證的時候尚需提供一種有序的驗證方式,這就提出了組序列的概念。

一個組可以定義為其他組的序列,使用它進行驗證的時候必須符合該序列規定的順序。在使用組序列驗證的時候,如果序列前邊的組驗證失敗,則后面的組將不再給予驗證。

下例中聲明了組 GroupA.class,GroupB.class 和 Group.class,其中 default,GroupA,GroupB 均為 Group 的序列。

[java]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. public interface GroupA {  
  2. }  
  3.   
  4. public interface GroupB {  
  5. }  
  6.   
  7. @GroupSequence( { Default.class, GroupA.class, GroupB.class })  
  8. public interface Group {  
  9. }  
  10.   
  11. public class User {  
  12.     @NotEmpty(message = "firstname may be empty")  
  13.     private String firstname;  
  14.   
  15.     @NotEmpty(message = "middlename may be empty", groups = Default.class)  
  16.     private String middlename;  
  17.   
  18.     @NotEmpty(message = "lastname may be empty", groups = GroupA.class)  
  19.     private String lastname;  
  20.   
  21.     @NotEmpty(message = "country may be empty", groups = GroupB.class)  
  22.     private String country;  
  23. }  
[java]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. @RequestMapping(value = "/update.action", method = RequestMethod.POST)  
  2. public String register(@Validated(Group.class) User user, BindingResult result) {  
  3.     if (result.hasErrors()) {  
  4.         return "validate/error";  
  5.     }  
  6.     return "redirect:/success";  
  7. }  

3. 驗證多個對象

當我們在一個功能處理方法上需要驗證多個模型對象時,需要通過如下形式來獲取驗證結果:

[java]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. @RequestMapping("/validate/multi")  
  2. public String multi(@Valid @ModelAttribute("a") A a, BindingResult aErrors, @Valid @ModelAttribute("b") B b, BindingResult bErrors) {  
  3.   
  4.     if (aErrors.hasErrors()) { //如果a模型對象驗證失敗  
  5.         return "validate/error";  
  6.     }  
  7.     if (bErrors.hasErrors()) { //如果a模型對象驗證失敗  
  8.         return "validate/error";  
  9.     }  
  10.     return "redirect:/success";  
  11. }  

每一個模型對象后邊都需要跟一個Errors或BindingResult對象來保存驗證結果,其方法體內部可以使用這兩個驗證結果對象來選擇出錯時跳轉的頁面或處理的邏輯。

4. Junit測試

當自定義拓展Validation時,可以使用如下方法進行測試:

[java]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. @Test  
  2. public void testValidate() {  
  3.     AnnotationDescriptor<EqualsAny> descriptor = new AnnotationDescriptor<EqualsAny>(EqualsAny.class);  
  4.     EqualsAny equalsAny = AnnotationFactory.create(descriptor);  
  5.     EqualsAnyValidator equalsAnyValidator = new EqualsAnyValidator();  
  6.     equalsAnyValidator.initialize(equalsAny);  
  7.     Assert.assertTrue(equalsAnyValidator.isValid("123", null));  
  8. }  

另外再講一點Spring對自定義JSR-303限制類型支持的新特性,那就是Spring支持往ConstraintValidator里面注入bean對象。例如在EqualsAnyValidator中利用@Resource注解注入其他Bean對象。

 

在使用Validation時,傳遞參數到國際化資源文件properties

 

 在CODE上查看代碼片派生到我的代碼片
  1. @NotEmpty(message="{password.empty.error}")  
  2. private String password;  

資源文件validation_zh_CN.properties中為

[plain]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. password.empty.error=password不能為空  

實際開發中,很多參數都是要驗證非空的,如果每個參數都單獨加個錯誤描述,是很麻煩的。properties雖支持“{}”的寫法傳遞參數,但使用JSR-303注解無法實現傳遞參數。我想了個辦法可通過自定義注解方式實現。

 

首先,建立個自定義的@NotEmpty注解:

[java]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. package com.itkt.payment.core.annotation;  
  2.   
  3. import java.lang.annotation.ElementType;  
  4. import java.lang.annotation.Retention;  
  5. import java.lang.annotation.RetentionPolicy;  
  6. import java.lang.annotation.Target;  
  7.   
  8. import javax.validation.Constraint;  
  9. import javax.validation.Payload;  
  10.   
  11. import com.itkt.payment.core.handler.NotEmptyValidator;  
  12.   
  13. @Retention(RetentionPolicy.RUNTIME)  
  14. @Target( { ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER })  
  15. @Constraint(validatedBy = { NotEmptyValidator.class })  
  16. public @interface NotEmpty {  
  17.   
  18.     String field() default "";  
  19.   
  20.     String message() default "{com.itkt.payment.core.handler.NotEmpty.message}";  
  21.   
  22.     Class<?>[] groups() default {};  
  23.   
  24.     Class<? extends Payload>[] payload() default {};  
  25. }  

自定義的NotEmpty注解中,我們新加了field字段,用於標識字段名稱。

然后,建立NotNullValidator實現類:

[java]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. package com.itkt.payment.core.handler;  
  2.   
  3. import javax.validation.ConstraintValidator;  
  4. import javax.validation.ConstraintValidatorContext;  
  5.   
  6. import com.itkt.payment.core.annotation.NotNull;  
  7.   
  8. public class NotNullValidator implements ConstraintValidator<NotNull, Object> {  
  9.   
  10.     @Override  
  11.     public void initialize(NotNull annotation) {  
  12.     }  
  13.   
  14.     @Override  
  15.     public boolean isValid(Object str, ConstraintValidatorContext constraintValidatorContext) {  
  16.         return str != null;  
  17.     }  
  18.   
  19. }  

之后,在資源文件validation_zh_CN.properties中,改變寫法:

[plain]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. password.empty.error={field}不能為空  

最后,我們就可以在User類中使用自定義的NotEmpty注解:

[java]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. @NotEmpty(field = "password", message = "{password.empty.error}")  
  2. private String password;  

 

實際上,國際化資源文件本身支持從JSR-303注解中獲取屬性的參數值的,例如從@Length注解中,獲取min和max屬性的值:

[plain]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. username.length.error=用戶名長度必須在{min}-{max}之間  

之所以自帶的@NotEmpty注解無法實現,是因為沒有一個屬性能傳遞字段名,所以通過自定義@NotEmpty注解來拓展個field字段。

 
 
@AssertTrue   //用於boolean字段,該字段只能為true   
@AssertFalse //該字段的值只能為false   
@CreditCardNumber //對信用卡號進行一個大致的驗證   
@DecimalMax //只能小於或等於該值   
@DecimalMin //只能大於或等於該值   
@Digits (integer= 2 ,fraction= 20 ) //檢查是否是一種數字的整數、分數,小數位數的數字。   
@Email //檢查是否是一個有效的email地址   
@Future //檢查該字段的日期是否是屬於將來的日期   
@Length (min=,max=) //檢查所屬的字段的長度是否在min和max之間,只能用於字符串   
@Max //該字段的值只能小於或等於該值   
@Min //該字段的值只能大於或等於該值   
@NotNull //不能為null   
@NotBlank //不能為空,檢查時會將空格忽略   
@NotEmpty //不能為空,這里的空是指空字符串   
@Null //檢查該字段為空   
@Past //檢查該字段的日期是在過去   
@Size (min=, max=) //檢查該字段的size是否在min和max之間,可以是字符串、數組、集合、Map等   
@URL (protocol=,host,port) //檢查是否是一個有效的URL,如果提供了protocol,host等,則該URL還需滿足提供的條件   
@Valid //該注解只要用於字段為一個包含其他對象的集合或map或數組的字段,或該字段直接為一個其他對象的引用,   
       //這樣在檢查當前對象的同時也會檢查該字段所引用的對象  

以下是分類
Bean Validation 中內置的 constraint
 
@Null     被注釋的元素必須為 null
@NotNull     被注釋的元素必須不為 null
@AssertTrue     被注釋的元素必須為 true
@AssertFalse     被注釋的元素必須為 false
@Min(value)     被注釋的元素必須是一個數字,其值必須大於等於指定的最小值
@Max(value)     被注釋的元素必須是一個數字,其值必須小於等於指定的最大值
@DecimalMin(value)     被注釋的元素必須是一個數字,其值必須大於等於指定的最小值
@DecimalMax(value)     被注釋的元素必須是一個數字,其值必須小於等於指定的最大值
@Size(max=, min=)     被注釋的元素的大小必須在指定的范圍內
@Digits (integer, fraction)     被注釋的元素必須是一個數字,其值必須在可接受的范圍內
@Past     被注釋的元素必須是一個過去的日期
@Future     被注釋的元素必須是一個將來的日期
@Pattern(regex=,flag=)     被注釋的元素必須符合指定的正則表達式

Hibernate Validator 附加的 constraint
@NotBlank(message =)   驗證字符串非null,且長度必須大於0
@Email     被注釋的元素必須是電子郵箱地址
@Length(min=,max=)     被注釋的字符串的大小必須在指定的范圍內
@NotEmpty     被注釋的字符串的必須非空
@Range(min=,max=,message=)     被注釋的元素必須在合適的范圍內


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM