列挙型メンバーフィールドを検証するためにhibernateアノテーションをどのように使用できますか?以下は機能しません。
enum UserRole {
USER, ADMIN;
}
class User {
@NotBlank //HV000030: No validator could be found for type: UserRole.
UserRole userRole;
}
文字列が列挙型の一部であることを確認するバリデーターを作成することもできます。
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);
}
}
}
「間違った値」の情報を失うことがないので、これは素晴らしいです。次のようなメッセージを受け取ることができます
値「someBadUserType」は有効なUserTypeではありません。有効なUserType値は、PERSON、COMPANYです。
編集
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);
}
}
}
エラーメッセージをカスタマイズして適切な値を表示するには、これを確認してください: https://stackoverflow.com/a/19833921/82609
注釈付き文字列がnullまたは空でないことを検証します。 NotEmptyとの違いは、末尾の空白が無視されることです。
UserRole
は文字列ではなく、object
は@NotNull
を使用します
注釈付き要素はnullであってはなりません。任意のタイプを受け入れます。
上記の Sebastienの回答 とより密接に関連しており、コードの行数が少なく、rawtypes
警告を犠牲にしてEnumSet.allOf
を使用していると思います
public enum FuelTypeEnum {DIESEL, PETROL, ELECTRIC, HYBRID, ...};
public enum BodyTypeEnum {VAN, COUPE, MUV, JEEP, ...};
@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();
}
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;
}
}
次に、フィールドに次のように注釈を付けます
@ValidateEnum(targetClassType = FuelTypeEnum.class, message = "Please select ...."
private String fuelType;
@ValidateEnum(targetClassType = BodyTypeEnum.class, message = "Please select ...."
private String bodyType;
上記は、Hibernate Validator
が設定されていて、デフォルトのアノテーションを使用していることを前提としています。
多くの場合、列挙型に変換しようとするのは名前だけではありません(これは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);
});
}
}
次のように使用します。
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;
}
上記の実装では、Springフレームワークの ReflectionUtils とJava 8+。