java - example - Validador de Hibernate: agregue un Dynamic ConstraintValidator
bean validation (3)
Después de conocer Hibernate Custom Validators , me ha interesado un tema, ¿podría crear una anotación base en la que pudiera establecer qué Validator usar?
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = validator().class)
public @interface CustomAnnotation {
public String message();
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
Class<? extends ConstraintValidator<? extends CustomAnnotation, Serializable>> validator();
}
Para poder usar @CustomAnnotation
de esta manera
@CustomAnnotation(validator = CustomConstraintValidator.class, message = "validationMessage")
private Object fieldName;
No creo que pueda implementar un resolvedor de validadores dinámico además del soporte de Hibernate Validator. Es mucho mejor tener un conjunto dedicado de pares de validación y anotación, por lo que cuando anota un campo con una anotación de Validación específica, queda claro qué Validador se va a usar.
No lo recomendaría pero puedes hacerlo más o menos de la siguiente manera:
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = GenericValidatorBootstrapperValidator.class)
public @interface CustomAnnotation {
public String message();
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
Class<? extends ConstraintValidator<? extends CustomAnnotation, Serializable>> validator();
}
public class GenericValidatorBootstrapperValidator implements ConstraintValidator<CustomAnnotation, Object> {
private final ConstraintValidator validator;
@Override
public void initialize(CustomAnnotation constraintAnnotation) {
Class<? extends ConstraintValidator> validatorClass = constraintAnnotation.validator();
validator = validatorClass.newInstance();
validator.initialize( ... ); //TODO with what?
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
return validator.isValid(value, context);
}
}
Pero de nuevo, prefieren anotaciones específicas, son más expresivas.
Editar
Después de su comentario, creo que lo que desea es poder establecer diferentes validadores según el tipo de devolución de la propiedad.
@CustomAnnotation
List<String> foo;
@CustomAnnotation
Table bar;
Si ese es el caso, agregue varias implementaciones de validadores en la anotación @Constraint
.
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {ListValidatorImpl.class, TableValidatorImpl.class, ...})
public @interface CustomAnnotation {
public String message();
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class ListValidatorImpl implements ConstraintValidator<CustomAnnotation, List> {
@Override
public boolean isValid(List value, ConstraintValidatorContext context) {
}
}
public class TableValidatorImpl implements ConstraintValidator<CustomAnnotation, Table> {
@Override
public boolean isValid(Table value, ConstraintValidatorContext context) {
}
}
Incluso puede vincular una anotación contraint con una implementación a través del archivo META/validation.xml
<constraint-mappings
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/mapping validation-mapping-1.1.xsd"
xmlns="http://jboss.org/xml/ns/javax/validation/mapping" version="1.1">
<constraint-definition annotation="org.mycompany.CustomAnnotation">
<validated-by include-existing-validators="true">
<value>org.mycompany.EnumCustomValidatorImpl</value>
</validated-by>
</constraint-definition>
</constraint-mappings>
Si necesita algo más flexible, creo que mi propuesta inicial funcionaría. En el método GenericValidatorBootstrapperValidator
isValid, puede llamar a la instancia de validador correcta en función del tipo de objeto del parámetro de value
(por instanceof
mediante instanceof
).
Hibernate Validator también ofrece ahora una anotación @ScriptAssert que facilita la implementación de validaciones personalizadas y ayuda a evitar muchas líneas de código.
Ejemplo de uso:
@ScriptAssert(lang = "javascript",
script = "_this.capital.equals(_this.capital.toUpperCase)",
message = "capital has not Capital letters")
public class BigLetters {
private String capital;
public String getCapital() {
return capital;
}
public void setCapital(String capital) {
this.capital = capital;
}
}