【问题标题】:Hibernate validation annotation - validate that at least one field is not nullHibernate 验证注解 - 验证至少一个字段不为空
【发布时间】:2012-08-31 08:38:30
【问题描述】:

有没有一种方法可以使用 here 定义的注释来定义 Hibernate 验证规则,说明至少一个字段不得为空?

这是一个假设的例子(@OneFieldMustBeNotNullConstraint 并不存在):

@Entity
@OneFieldMustBeNotNullConstraint(list={fieldA,fieldB})
public class Card {

    @Id
    @GeneratedValue
    private Integer card_id;

    @Column(nullable = true)
    private Long fieldA;

    @Column(nullable = true)
    private Long fieldB;

}

在图示的情况下,fieldA 可以为 null 或 fieldB 可以为 null,但不能同时为 null。

一种方法是创建我自己的验证器,但如果它已经存在,我想避免。如果您已经制作了一个验证器,请分享一个...谢谢!

【问题讨论】:

    标签: java hibernate annotations validation


    【解决方案1】:

    我终于写出了整个验证器:

    import static java.lang.annotation.ElementType.TYPE;
    import static java.lang.annotation.RetentionPolicy.RUNTIME;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.Retention;
    import java.lang.annotation.Target;
    
    import javax.validation.Constraint;
    import javax.validation.ConstraintValidator;
    import javax.validation.ConstraintValidatorContext;
    import javax.validation.Payload;
    
    import org.apache.commons.beanutils.PropertyUtils; 
    
    @Target( { TYPE })
    @Retention(RUNTIME)
    @Constraint(validatedBy = CheckAtLeastOneNotNull.CheckAtLeastOneNotNullValidator.class)
    @Documented
    public @interface CheckAtLeastOneNotNull {
        
         String message() default "{com.xxx.constraints.checkatleastnotnull}";
    
         Class<?>[] groups() default {};
    
         Class<? extends Payload>[] payload() default {};
            
         String[] fieldNames();
            
         public static class CheckAtLeastOneNotNullValidator implements ConstraintValidator<CheckAtLeastOneNotNull, Object> {
                
             private String[] fieldNames;
    
             public void initialize(CheckAtLeastOneNotNull constraintAnnotation) {
                 this.fieldNames = constraintAnnotation.fieldNames();
             }
    
             public boolean isValid(Object object, ConstraintValidatorContext constraintContext) {
    
                 if (object == null) {
                     return true;
                 }
                 try { 
                     for (String fieldName:fieldNames){
                         Object property = PropertyUtils.getProperty(object, fieldName);
                            
                         if (property != null) return true;
                     }
                     return false;
                 } catch (Exception e) {
                     return false;
                 }
             }
         }
    }
    

    使用示例:

    @Entity
    @CheckAtLeastOneNotNull(fieldNames={"fieldA","fieldB"})
    public class Reward {
    
        @Id
        @GeneratedValue
        private Integer id;
    
        private Integer fieldA;
        private Integer fieldB;
    
        [...] // accessors, other fields, etc.
    }
    

    【讨论】:

      【解决方案2】:

      只需编写您自己的验证器。不应该很简单:遍历字段名称并使用反射获取字段值。

      概念:

      Collection<String> values = Arrays.asList(
          BeanUtils.getProperty(obj, fieldA),
          BeanUtils.getProperty(obj, fieldB),
      );
      
      return CollectionUtils.exists(values, PredicateUtils.notNullPredicate());
      

      我使用了来自commons-beanutilscommons-collections 的方法。

      【讨论】:

      • 谢谢,这帮助我使用 PropertyUtils.getProperty 编写了自省部分。
      【解决方案3】:

      这有点像Resh32 的回答,但这也会将验证消息与验证对象的特定字段绑定。

      验证注释类将如下所示。

      import javax.validation.Constraint;
      import javax.validation.Payload;
      import java.lang.annotation.Documented;
      import java.lang.annotation.Retention;
      import java.lang.annotation.Target;
      
      import static java.lang.annotation.ElementType.TYPE;
      import static java.lang.annotation.RetentionPolicy.RUNTIME;
      
      /**
       * @author rumman
       * @since 9/23/19
       */
      @Target(TYPE)
      @Retention(RUNTIME)
      @Documented
      @Constraint(validatedBy = NotNullAnyValidator.class)
      public @interface NotNullAny {
      
          String[] fieldNames();
      
          String errorOnProperty();
      
          String messageKey() default "{error.required}";
      
          Class<?>[] groups() default {};
      
          Class<? extends Payload>[] payload() default {};
      }
      

      验证器类如下所示。

      import javax.validation.ConstraintValidator;
      import javax.validation.ConstraintValidatorContext;
      import java.lang.reflect.InvocationTargetException;
      import java.util.Arrays;
      import java.util.Objects;
      
      import static org.springframework.beans.BeanUtils.getPropertyDescriptor;
      
      /**
       * @author rumman
       * @since 9/23/19
       */
      public class NotNullAnyValidator implements ConstraintValidator<NotNullAny, Object> {
      
          private String[] fieldNames;
          private String errorOnProperty;
          private String messageKey;
      
          @Override
          public void initialize(NotNullAny validateDateRange) {
              fieldNames = validateDateRange.fieldNames();
              errorOnProperty = validateDateRange.errorOnProperty();
              messageKey = validateDateRange.messageKey();
          }
      
          @Override
          public boolean isValid(Object obj, ConstraintValidatorContext validatorContext) {
      
              Object[] fieldValues = new Object[fieldNames.length];
      
              try {
                  for (int i = 0; i < fieldValues.length; i++) {
                      fieldValues[i] = getPropertyDescriptor(obj.getClass(), fieldNames[i]).getReadMethod().invoke(obj);
                  }
      
              } catch (IllegalAccessException | InvocationTargetException e) {
                  throw new RuntimeException(e);
              }
      
              if (Arrays.stream(fieldValues).noneMatch(Objects::nonNull)) {
                  validatorContext.buildConstraintViolationWithTemplate(messageKey)
                          .addPropertyNode(errorOnProperty)
                          .addConstraintViolation()
                          .disableDefaultConstraintViolation();
      
                  return false;
              }
      
              return true;
          }
      }
      

      注意最后一个if 条件块,它检查是否没有找到非null 值然后指定错误消息,错误消息将绑定到的属性并将添加约束冲突。

      在类中使用注解

      /**
       * @author rumman
       * @since 9/23/19
       */
      @NotNullAny(fieldNames = {"field1", "field2", "field3"},
              errorOnProperty = "field1",
              messageKey = "my.error.msg.key")
      public class TestEntityForValidation {
      
          private String field1;
          private String field2;
          private String field3;
      
          // standard constructor(s) and getter & setters below
      }
      

      【讨论】:

        猜你喜欢
        • 2021-02-04
        • 2014-06-17
        • 2011-06-25
        • 2011-07-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多