Java 如何在枚举中使用 Hibernate 验证注释?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18205787/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 23:18:40  来源:igfitidea点击:

How to use Hibernate validation annotations with enums?

javahibernateannotations

提问by membersound

How can I use hibernate annotations to validate an enum member field? The following does not work:

如何使用休眠注释来验证枚举成员字段?以下不起作用:

enum UserRole {
   USER, ADMIN;
}

class User {
   @NotBlank //HV000030: No validator could be found for type: UserRole.
   UserRole userRole;
}

采纳答案by Sebastien Lorber

Note you can also create a validator to check a String is part of an enumeration.

请注意,您还可以创建一个验证器来检查字符串是否是枚举的一部分。

public enum UserType { PERSON, COMPANY }

@NotNull
@StringEnumeration(enumClass = UserCivility.class)
private String title;


@Documented
@Constraint(validatedBy = StringEnumerationValidator.class)
@Target({ METHOD, FIELD, ANNOTATION_TYPE, PARAMETER, CONSTRUCTOR })
@Retention(RUNTIME)
public @interface StringEnumeration {

  String message() default "{com.xxx.bean.validation.constraints.StringEnumeration.message}";
  Class<?>[] groups() default {};
  Class<? extends Payload>[] payload() default {};

  Class<? extends Enum<?>> enumClass();

}


public class StringEnumerationValidator implements ConstraintValidator<StringEnumeration, String> {

  private Set<String> AVAILABLE_ENUM_NAMES;

  @Override
  public void initialize(StringEnumeration stringEnumeration) {
    Class<? extends Enum<?>> enumSelected = stringEnumeration.enumClass();
    //Set<? extends Enum<?>> enumInstances = EnumSet.allOf(enumSelected);
    Set<? extends Enum<?>> enumInstances = Sets.newHashSet(enumSelected.getEnumConstants());
    AVAILABLE_ENUM_NAMES = FluentIterable
            .from(enumInstances)
            .transform(PrimitiveGuavaFunctions.ENUM_TO_NAME)
            .toSet();
  }

  @Override
  public boolean isValid(String value, ConstraintValidatorContext context) {
    if ( value == null ) {
      return true;
    } else {
      return AVAILABLE_ENUM_NAMES.contains(value);
    }
  }

}


This is nice because you don't loose the information of the "wrong value". You can get a message like

这很好,因为您不会丢失“错误值”的信息。您可以收到类似的消息

The value "someBadUserType" is not a valid UserType. Valid UserType values are: PERSON, COMPANY

值“someBadUserType”不是有效的用户类型。有效的 UserType 值为:PERSON、COMPANY



Edit

编辑

For those who want a non-Guava version it should work with something like:

对于那些想要非 Guava 版本的人来说,它应该适用于以下内容:

public class StringEnumerationValidator implements ConstraintValidator<StringEnumeration, String> {

  private Set<String> AVAILABLE_ENUM_NAMES;

  public static Set<String> getNamesSet(Class<? extends Enum<?>> e) {
     Enum<?>[] enums = e.getEnumConstants();
     String[] names = new String[enums.length];
     for (int i = 0; i < enums.length; i++) {
         names[i] = enums[i].name();
     }
     Set<String> mySet = new HashSet<String>(Arrays.asList(names));
     return mySet;
  }

  @Override
  public void initialize(StringEnumeration stringEnumeration) {
    Class<? extends Enum<?>> enumSelected = stringEnumeration.enumClass();
    AVAILABLE_ENUM_NAMES = getNamesSet(enumSelected);
  }

  @Override
  public boolean isValid(String value, ConstraintValidatorContext context) {
    if ( value == null ) {
      return true;
    } else {
      return AVAILABLE_ENUM_NAMES.contains(value);
    }
  }

}

And to customize the error message and display the appropriate values, check this: https://stackoverflow.com/a/19833921/82609

要自定义错误消息并显示适当的值,请检查:https: //stackoverflow.com/a/19833921/82609

回答by Suresh Atta

@NotBlank

@NotBlank

Validate that the annotated string is not null or empty. The difference to NotEmpty is that trailing whitespaces are getting ignored.

验证带注释的字符串不为 null 或为空。NotEmpty 的不同之处在于尾随空格被忽略。

Where as UserRoleis not String and an objectUse @NotNull

asUserRole不是 String 和objectUse@NotNull

The annotated element must not be null. Accepts any type.

带注释的元素不能为空。接受任何类型。

回答by Raf

I suppose a more closely related to Sebastien's answerabove with fewer lines of code and makes use of EnumSet.allOfin the expense of a rawtypeswarning

我认为与上面塞巴斯蒂安的答案更密切相关,代码行更少,并且EnumSet.allOfrawtypes警告为代价使用

Enums setup

枚举设置

public enum FuelTypeEnum {DIESEL, PETROL, ELECTRIC, HYBRID, ...}; 
public enum BodyTypeEnum {VAN, COUPE, MUV, JEEP, ...}; 

Annotation setup

注释设置

@Target(ElementType.FIELD) //METHOD, CONSTRUCTOR, etc.
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EnumValidator.class)
public @interface ValidateEnum {
    String message() default "{com.xxx.yyy.ValidateEnum.message}";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    Class<? extends Enum<?>> targetClassType();
}

Validator setup

验证器设置

public class EnumValidator implements ConstraintValidator<ValidateEnum, String> {
    private Set<String> allowedValues;

    @SuppressWarnings({ "unchecked", "rawtypes" })
    @Override
    public void initialize(ValidateEnum targetEnum) {
        Class<? extends Enum> enumSelected = targetEnum.targetClassType();
        allowedValues = (Set<String>) EnumSet.allOf(enumSelected).stream().map(e -> ((Enum<? extends Enum<?>>) e).name())
                .collect(Collectors.toSet());
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return value == null || allowedValues.contains(value)? true : false; 
    }
} 

Now go ahead and annotate your fields as follow

现在继续注释您的字段如下

@ValidateEnum(targetClassType = FuelTypeEnum.class, message = "Please select ...." 
private String fuelType; 

@ValidateEnum(targetClassType = BodyTypeEnum.class, message = "Please select ...." 
private String bodyType; 

The above assumes you have the Hibernate Validatorsetup and working with default annotation.

以上假设您已Hibernate Validator设置并使用默认注释。

回答by Abhijit Sarkar

Often times, attempting to convert to an enum is not just by name (which is the default behavior with valueOfmethod). For example, what if you have enums representing DayOfWeekand you want an integer to be converted to a DayOfWeek? To do that, I created the following annotation:

很多时候,尝试转换为枚举不仅仅是通过名称(这是valueOf方法的默认行为)。例如,如果您有枚举表示DayOfWeek并且希望将整数转换为DayOfWeek? 为此,我创建了以下注释:

@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@Constraint(validatedBy = {ValidEnumValueValidator.class})
public @interface ValidEnumValue {
    String message() default "invalidParam";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    Class<? extends Enum<?>> value();

    String enumMethod() default "name";

    String stringMethod() default "toString";
}
public class ValidEnumValueValidator implements ConstraintValidator<ValidEnumValue, String> {
    Class<? extends Enum<?>> enumClass;
    String enumMethod;
    String stringMethod;

    @Override
    public void initialize(ValidEnumValue annotation) {
        this.enumClass = annotation.value();
        this.enumMethod = annotation.enumMethod();
        this.stringMethod = annotation.stringMethod();
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        Enum<?>[] enums = enumClass.getEnumConstants();
        Method method = ReflectionUtils.findMethod(enumClass, enumMethod);

        return Objects.nonNull(enums) && Arrays.stream(enums)
                .map(en -> ReflectionUtils.invokeMethod(method, en))
                .anyMatch(en -> {
                    Method m = ReflectionUtils.findMethod(String.class, stringMethod);
                    Object o = ReflectionUtils.invokeMethod(m, value);

                    return Objects.equals(o, en);
                });
    }
}

You'd use it as follows:

您可以按如下方式使用它:

public enum TestEnum {
        A("test");

        TestEnum(String s) {
            this.val = s;
        }

        private String val;

        public String getValue() {
            return this.val;
        }
    }
public static class Testee {
        @ValidEnumValue(value = TestEnum.class, enumMethod = "getValue", stringMethod = "toLowerCase")
        private String testEnum;
    }

Above implementation uses ReflectionUtilsfrom Spring framework and Java 8+.

以上实现使用来自 Spring 框架和 Java 8+ 的ReflectionUtils