Search code examples
javahibernateannotations

How to use Hibernate validation annotations with enums?


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

Solution

  • 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


    Edit

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

    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