‘jsr 303 Bean Validation’

Encontrando las validaciones configuradas en un bean

Tuesday, July 6th, 2010 Autor : gmateo

Código Fuente: Ejemplo 3

A continuación vamos a revisar las clases y los métodos que nos permiten obtener información de las distintas validaciones que han sido configuradas en un bean.

Para tener acceso a las clases necesarias necesitamos usar la referencia del objeto Validator. Recordar que una manera simple de obtener esta referencia es hacer lo siguiente:

ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
Validator validator = validatorFactory.getValidator();

BeanDescriptor

Esta es la clase principal desde la cual podemos tener acceso a la información referente a las validaciones configuradas de un bean.

Para tener acceso a este objeto necesitamos llamar al método: “getConstraintsForClass” del “validator” enviando como parámetro la clase del bean del que deseamos obtener la información.

BeanDescriptor puertaBeanDescriptor = validator.getConstraintsForClass(Puerta.class);
 
Ya teniendo la referencia al “BeanDescriptor” podemos usar su métodos:

isBeanConstrained()

Este método nos indica si el bean tiene o no configurado alguna validación. Por ejemplo, teniendo en cuenta la definición de la clase Puerta

public class Puerta {
}

el método va a devolver false.

BeanDescriptor puertaBeanDescriptor = validator.getConstraintsForClass(Puerta.class);
System.out.println("Puerta tiene algún constraint:" + puertaBeanDescriptor.isBeanConstrained());

hasConstraints()

Este método nos indica si existen “constraints” a nivel de clase. Por ejemplo asumiendo la siguiente definición de la clase Carro:

public class Carro {
private String codigo;
@NotNull(groups=GrupoBasico.class)
private String numeroPlaca;
@NotNull
private String anio;
@Valid
private Motor motor;

}

el método va a devolver false:

BeanDescriptor carroBeanDescriptor = validator.getConstraintsForClass(Carro.class);
System.out.println("Carro tiene algún constraint de Clase:" + carroBeanDescriptor.hasConstraints());

getConstraintDescriptors()

Este método nos devuelve todos los constraints a nivel de clase que existen, para el caso de la clase Carro nos devolverá un Set de tamaño CERO.

getConstrainedProperties()

Nos devuelve todos los constraints aplicados a los atributos que existen en un bean. Por ejemplo para la clase Carro el tamaño del siguiente Set será 3.

Set<PropertyDescriptor> constraintProperties = carroBeanDescriptor.getConstrainedProperties();
System.out.println("Carro tiene constraints a nivel de Atributos Nro:" + constraintProperties.size());

getConstraintsForProperty(String property)

Nos devuelve un PropertyDescriptor el cual es un objeto que puede tener uno o más ConstraintDescriptors. Por ejemplo para la clase Carro tendríamos lo siguiente:

Para el atributo “codigo”, este método devolverá null, ya que este atributo no tiene ninguna anotación de validación.

PropertyDescriptor codigoPropertyDescriptor = carroBeanDescriptor.getConstraintsForProperty("codigo");
System.out.println("Código property descriptor:" + codigoPropertyDescriptor);

Pero para el atributo "numeroPlaca" si devolverá un objeto con toda la información de validación de ese atributo.

PropertyDescriptor nroPlacaPropertyDescriptor = carroBeanDescriptor.getConstraintsForProperty("numeroPlaca");
System.out.println("Número de Placa property descriptor:" + nroPlacaPropertyDescriptor);

PropertyDescriptor

Contiene toda la información de validación de un atributo de un bean. Comparte varios métodos con el “BeanDescriptor”, ya que ambos implementan la interface:”ElementDescriptor”.

getConstraintDescriptors()

Devuelve todos los ContraintDescriptors asociados a un atributo del bean. Por ejemplo, en el siguiente caso devuelve un Set con un ConstraintDescritpor.

PropertyDescriptor nroPlacaPropertyDescriptor = carroBeanDescriptor.getConstraintsForProperty("numeroPlaca");
System.out.println("Número de Placa property descriptor:" + nroPlacaPropertyDescriptor);
System.out.println("Número de Placa property constraint descriptors:" + nroPlacaPropertyDescriptor.getConstraintDescriptors());

ConstraintDescriptor

Es la clase que tiene toda la información del constraint.

Por ejemplo para el atributo “numeroPlaca” podemos obtener este objeto de la siguiente manera:

PropertyDescriptor nroPlacaPropertyDescriptor = carroBeanDescriptor.getConstraintsForProperty("numeroPlaca");
System.out.println("Número de Placa property descriptor:" + nroPlacaPropertyDescriptor);
ConstraintDescriptor nroPlacaConstraintDescriptor = nroPlacaPropertyDescriptor.getConstraintDescriptors().iterator().next();

Entre sus principales métodos tenemos:

getAnnotation()

Nos devuelve la anotación que usa este constraint.

getAttributes()

Devuelve un Map con todos los atributos seteados en la anotación del constraint.

getGroups()

Devuelve los grupos a los que pertenece el constraint.

getPayload()

Devuelve el payload asociado al constraint.

A tener en Cuenta

Para poder encontrar un constraint teniendo en cuenta algunos filtros podemos usar el método “findConstraints()”, tanto en el “BeanDescriptor” como en el “PropertyDescriptor”

Por ejemplo en el siguiente código estamos buscando todos los constraints que estén configurados en el atributo “numeroPlaca”

Set<ConstraintDescriptor<?>> nroPlacaConstraintDescriptors = 
nroPlacaPropertyDescriptor.findConstraints().
declaredOn(ElementType.FIELD).
getConstraintDescriptors();
System.out.println("Número de Placa Constraint Descriptors:"+nroPlacaConstraintDescriptors);

Creando custom constraints

Sunday, July 4th, 2010 Autor : gmateo

Código Fuente: Ejemplo 1

Hibernate Validator viene con una serie de constraints ya creados, estos incluyen a los constraints que son parte del jsr303. En el siguiente link podemos encontrar una descripción de estos Constraints.

Por otro lado, nosotros también podemos crear nuestros propios constraints, los pasos necesarios para hacer esto, serán descritos a continuación.

Creando constraints simples

Vamos a crear dos constraints, uno a nivel de atributo y otro a nivel de clase.

Constraint a nivel de atributo

Vamos a crear un constraint que valide que un atributo de tipo String sólo tenga letras. Este constraint también debería poderse colocar al “getter” del atributo.

Necesitamos hacer lo siguiente:

- Crear la anotación que será usada para las validaciones.
   1: @Target({METHOD, FIELD, ANNOTATION_TYPE})

   2: @Retention(RUNTIME)

   3: @Constraint(validatedBy = OnlyLettersValidator.class)

   4: @Documented

   5: public @interface OnlyLetters {

   6:     String message() default "com.aw.jsr303.ejemplo001.constraints.onlyletters";

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

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

   9: }

Línea 1: Se indica que la anotación puede usarse tanto en atributos, métodos y anotaciones (esto es necesario para crear constraint compuestos).

Línea 3:  Se define que la anotación será un constraint y que la clase que se encargará de realizar la validación será: “OnlyLettersValidator”

Línea 6: Se define el mensaje por default que se usará cuando falle esta validación.

Línea 7: Se define los grupos a los cuales puede pertenecer este constraint.

Línea 8:  Se define los “payload” que es una manera de agregar mayor información al Constraint, por ejemplo podríamos definir algunas interfaces que indiquen el nivel de severidad del constraint y enviar esta información como payload.

- Crear la clase que se encargue de ejecutar la validación.
   1: public class OnlyLettersValidator implements ConstraintValidator<OnlyLetters, String> {

   2:     public void initialize(OnlyLetters annotation) {

   3:     }

   4:     public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {

   5:         return allAreLetters(value);

   6:     }

   7:     private boolean allAreLetters(String str) {

   8:         if (str == null) {

   9:             return false;

  10:         }

  11:         int stringSize = str.length();

  12:         for (int i = 0; i < stringSize; i++) {

  13:             if (Character.isLetter(str.charAt(i)) == false) {

  14:                 return false;

  15:             }

  16:         }

  17:         return true;

  18:     }

  19: }

Línea 1: La clase que finalmente realizará la validación debe implementar la interface “ConstraintValidator” y debe poner como tipos, la anotación que usará para el constraint “OnlyLetters” y el tipo de dato que será validado “String” (ConstraintValidator)

Línea 2: Este método será llamado al inicio para obtener la información que se necesite de la anotación.

Línea 4: Este método es el que realiza la validación, en este caso va a recibir como parámetro el valor del atributo a ser validado.

Modo de uso del constraint
public class ComprobanteDePago {
@OnlyLetters
private String codigoInterno;

Constraint a nivel de clase

Vamos a crear un constraint a nivel de clase. Se validará que la clase ComprobanteDePago tenga obligatoriamente seteado el RUC siempre y cuando el tipo de documento sea FACTURA.

Necesitamos hacer lo siguiente:

- Crear la anotación que será usada para las validaciones.
   1: @Target({TYPE})

   2: @Retention(RUNTIME)

   3: @Constraint(validatedBy = RucInfoValidator.class)

   4: @Documented

   5: public @interface RucInfoChecker {

   6:     String message() default "com.aw.jsr303.ejemplo001.constraint.rucinfochecker";

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

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

   9: }

Línea 1: Se indica que la anotación sólo se puede usar a nivel de clase.

Línea 3:  Se define que la anotación será un constraint y que la clase que se encargará de realizar la validación será: “RucInfoValidator”

- Crear la clase que se encargue de ejecutar la validación.
   1: public class RucInfoValidator implements ConstraintValidator<RucInfoChecker, ComprobanteDePago> {

   2:     public void initialize(RucInfoChecker annotation) {

   3:     }

   4:     public boolean isValid(ComprobanteDePago comprobanteDePago, ConstraintValidatorContext constraintValidatorContext) {

   5:         if (comprobanteDePago.getTipo() == ComprobanteTipo.FACTURA){

   6:             String ruc =comprobanteDePago.getRuc();

   7:             return ruc != null && !"".equals(ruc);

   8:         }

   9:         return true;

  10:     }

  11: }

Línea 1: La clase que finalmente realizará la validación debe implementar la interface “ConstraintValidator” y debe poner como tipos, la anotación que usará para el constraint “RucInfoChecker” y el tipo de dato que será validado “ComprobanteDePago” (ConstraintValidator)

Línea 2: Este método será llamado al inicio para obtener la información que se necesite de la anotación.

Línea 4: Este método es el que realiza la validación, en este caso va a recibir como parámetro la instancia de la clase DocumentoDePago.

Modo de uso del constraint
@RucInfoChecker
public class ComprobanteDePago {

Creando constraints compuestos

Los constraints compuestos son aquellos que agrupan más de un constraint. A continuación vamos a crear un constraint que agrupe @NotNull y @OnlyLetters. En estos casos sólo es necesario crear la anotación para el nuevo constraint.

   1: @NotNull

   2: @OnlyLetters

   3: @Target({METHOD, FIELD})

   4: @Retention(RUNTIME)

   5: @Constraint(validatedBy = {})

   6: @Documented

   7: @ReportAsSingleViolation

   8: public @interface OnlyLettersNotNull {

   9:     String message() default "com.aw.jsr303.ejemplo001.constraints.onlylettersnotnull";

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

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

  12: }

Línea 1: Se indica que se incluirá el constraint @NotNull

Línea 2: Se indica que se incluirá el constraint @OnlyLetters

Línea 5:  Se define que la anotación será un constraint, no se indica la clase que será encargada de la validación, ya que es un constraint compuesto que en esencia sólo agrupa otros constraints.

Línea 7: Usando la anotación “@ReportAsSingleViolation” podemos indicar que por más que muchos constraints – que forman parte de este constraint compuesto – fallen, un sólo error debe ser reportado, sin esta anotación se reportarán todos los errores individuales que se encuentren.

Modo de uso del constraint

public class Item {
@OnlyLettersNotNull
private String coProducto;

Validar Beans – Agrupación de Validaciones

Saturday, July 3rd, 2010 Autor : gmateo

Código Fuente: Ejemplo 2

Para poder separar las validaciones de acuerdo a algún criterio debemos usar los grupos (“groups”)

Declaración de Grupos de validación

Normalmente para definir un grupo se usa una interface sin métodos, pero también se pueden usar clases. A continuación declararemos tres grupos de validación.

public interface TodasLasValidaciones {
}
public interface ValidacionesPrimerNivel {
}
public interface ValidacionesSegundoNivel {
}

Uso de los grupos de validación

Una vez definidos los grupos de validación los podemos usar en cualquier anotación. Por ejemplo:

   1: public class Ciudadano {

   2:     @NotNull(message = "Se debe setear el estado civil", groups = ValidacionesPrimerNivel.class)

   3:     private EstadoCivil estadoCivil;

   4:     @Min(value = 18, groups = ValidacionesSegundoNivel.class)

   5:     private int edad;

   6:     @NotNull

   7:     private String documentoDeIdentidad;

   8: }

Línea 2-3 : Acá estamos indicando que la validación de @NotNull pertenece al grupo “ValidacionesPrimerNivel”.

Línea 4-5 : Acá estamos indicando que la validación de @Min pertenece al grupo “ValidacionesSegundoNivel”.

Línea 6-7 : Acá estamos indicando que la validación de @NotNull pertenece al grupo “Default”, que es un grupo especial al cual pertenecen todas las validaciones cuyo grupo no ha sido seteado.

También se debe tener en cuenta que el valor de “groups” puede ser un arreglo con varios grupos.

Validar usando grupos

Los grupos se pueden usar en cualquiera de los métodos:”validate”, “validateProperty” o “validateValue” de la clase Validator.

Ejecutando las validaciones por defecto

   1: Validator validator = validatorFactory.getValidator();

   2: Ciudadano ciudadano = new Ciudadano();

   3: Set<ConstraintViolation<Ciudadano>> errors = validator.validate(ciudadano);

   4: System.out.println("Errores: Nro:<" + errors.size()+">" + errors);

   5: Set<ConstraintViolation<Ciudadano>> errorsDefault = validator.validate(ciudadano, Default.class);

   6: System.out.println("Errores usando Default:Nro:<" +  errorsDefault.size() +">" +errorsDefault);

Línea 3 : Se llama al método “validate” sin indicar ningún grupo.

Línea 5 : Se llama al método “validate” enviando como parámetro el grupo “Default”.

Se debe notar que la línea 3 y la línea 5 son equivalente en cuanto realizan la validación de todas las anotaciones que no tienen seteado explícitamente el atributo “groups”

Ejecutando las validaciones del primer nivel

Acá se van a ejecutar sólo las validaciones que pertenecen al grupo “ValidacionesPrimerNivel”.

Set<ConstraintViolation<Ciudadano>> errorsPrimerNivel = validator.validate(ciudadano,ValidacionesPrimerNivel.class);
System.out.println("Errores Primer Nivel:Nro:<" + errorsPrimerNivel.size()+">"+errorsPrimerNivel);

Podemos ver en el extracto anterior como enviamos el grupo como un parámetro más del método “validate”.

Ejecutando las validaciones del segundo nivel

Acá se van a ejecutar sólo las validaciones que pertenecen al grupo “ValidacionesSegundoNivel”.

Set<ConstraintViolation<Ciudadano>> errorsSegundoNivel = validator.validate(ciudadano,ValidacionesSegundoNivel.class);
System.out.println("Errores Segundo Nivel:Nro:<" + errorsSegundoNivel.size()+">"+errorsSegundoNivel);

Al igual que en caso anterior tenemos que enviar el grupo como parámetro al método “validate”

Ejecutar todas las validaciones

Para ejecutar todas las validaciones necesitamos enviar como parámetros del método “validate” todo los grupos que se han usado, incluyendo el “Default”, como se puede ver en el siguiente extracto:

Set<ConstraintViolation<Ciudadano>> errorsTodasLasValidaciones = validator.validate(ciudadano,Default.class,ValidacionesPrimerNivel.class,ValidacionesSegundoNivel.class);
System.out.println("Todos los Errores:Nro:<" + errorsTodasLasValidaciones.size()+">"+errorsTodasLasValidaciones);

Claro que  también podemos crear un grupo que extienda todos los grupos.

public interface SuperGrupo extends Default,
ValidacionesPrimerNivel,
ValidacionesSegundoNivel {
}

y ejecutar la validación usando este nuevo grupo.

Set<ConstraintViolation<Ciudadano>> errorsSuperGrupo = validator.validate(ciudadano,SuperGrupo.class);
System.out.println("Todas las Validaciones :Nro:<" + errorsSuperGrupo.size()+">"+errorsSuperGrupo);

Ejecutar los grupos de validaciones en secuencia

Acá lo que se desea hacer, es poder ejecutar las validaciones teniendo en cuenta un orden entre los grupos que se desean validar, además de eso se necesita que si un grupo genera errores de validación ya no se ejecute el siguiente grupo de validaciones.

Para poder hacer lo anterior usaremos la anotación: “@GroupSequence”. Veamos el siguiente ejemplo:

@GroupSequence({Default.class,ValidacionesPrimerNivel.class,ValidacionesSegundoNivel.class})
public interface TodasLasValidaciones {
}

Acá estamos definiendo el grupo: “TodasLasValidaciones” y como estamos usando la anotación “@GroupSequence” podemos decir que:

-  Este grupo incluye a los grupos: Default, ValidacionesPrimerNivel, ValidacionesSegundoNivel.

- Si fallan las validaciones de uno de los grupos, las validaciones de los siguientes grupos no serán ejecutadas. Por ejemplo si las validaciones del grupo Default fallan, las validaciones de los otros 2 grupos no serán ejecutadas.

Para ejecutar las validaciones de este grupo “TodasLasValidaciones” llamamos al método “validate” enviando como parámetro el grupo.

Set<ConstraintViolation<Ciudadano>> errorsTodasLasValidacionesEnSecuencia = validator.validate(ciudadano,TodasLasValidaciones.class);
System.out.println("Validación en secuencia:Nro:<" + errorsTodasLasValidacionesEnSecuencia.size()+">"+errorsTodasLasValidacionesEnSecuencia);

Modificando la secuencia de grupos de validación por defecto de una clase.

Para entender esto veamos el siguiente ejemplo:

Teniendo en cuenta la siguiente definición:

public class Carro {
@NotNull
private String numeroPlaca;

@NotNull(groups = ValidacionesPrimerNivel.class)
private Integer numeroAsientos;

Si ejecutamos lo siguiente:

Carro carro  = new  Carro();
Set<ConstraintViolation<Carro>> erroresEnCarro = validator.validate(carro);
System.out.println("Carro Errores: Nro:<" + erroresEnCarro.size()+">" + erroresEnCarro);

Solamente se ejecutará la validación del atributo “numeroPlaca” ya que tiene la anotación @NotNull  sin ningún grupo, por lo cual se asume que pertenece al grupo “Default”. La validación sobre “numeroAsientos” no será ejecutada ya que pertenece al grupo “ValidacionPrimerNivel”.

Ahora lo que deseamos es que al llamar a “validator.validate(carro)” se ejecuten tanto las validaciones del grupo “Default” como las del grupo “ValidacionPrimerNivel”. Para poder hacer esto debemos agregar la anotación “@GroupSequence” a la clase Carro, la cual quedaría así:

@GroupSequence({Carro.class, ValidacionesPrimerNivel.class})
public class Carro {

De esta manera al llamar a: “validator.validate(carro)”, primero se ejecutarían las validaciones por defecto, y si no hay errores luego se ejecutarían las validaciones del grupo “ValidacionPrimerNivel”.

Se debe notar que en este caso en la anotación “@GroupSequence” se está usando “Carro.class” para indicar el grupo “Default”.

Validar Beans

Friday, July 2nd, 2010 Autor : gmateo

Código Fuente: Ejemplo 1

Esta especificación considera una sola clase para realizar todas las validaciones. Esta clase es: “Validator” y la manera más simple de tener una referencia a este objeto es hacer lo siguiente:

ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
Validator validator = validatorFactory.getValidator();

Ahora bien, teniendo en cuenta la siguiente declaración:

public class ComprobanteDePago {
private Date fechaDeEntrega;
@Future
public Date getFechaDeEntrega() {
return fechaDeEntrega;
}
}

y teniendo la referencia al objeto Validator podemos llevar a cabo las siguientes validaciones:

Validar el valor de un atributo

Si sólo deseamos ejecutar la validación @Future que está marcando el “getter” del atributo “fechaDeEntrega” tendríamos que hacer lo siguiente:

Set<ConstraintViolation<ComprobanteDePago>> errorsProperty =  validator.validateProperty(comprobante,"fechaDeEntrega");
System.out.println("Errores del atributo <fechaDeEntrega>:"+ errorsProperty);

Acá estamos llamando al método “validateProperty” del validator, enviándole como parámetros la instancia del objeto y el nombre del atributo que se desea validar.

Validar si un valor cumple con las restricciones de un atributo

Si sólo deseamos validar que cierto valor cumple con las restricciones de un atributo, sin tener que setear el valor a la instancia del objeto debemos hacer lo siguiente:

Set<ConstraintViolation<ComprobanteDePago>> errorsPropertyValue =  validator.validateValue(ComprobanteDePago.class,"fechaDeEntrega",new Date());
System.out.println("Errores del valor que se quiere setear al atributo <fechaDeEntrega>:"+ errorsPropertyValue);

Acá estamos llamando al método “validateValue” del validator, enviándole como parámetro la clase, el nombre del atributo y el valor que se desea validar.

Validar todo el objeto

Para validar todas las restricciones asociadas a un objeto debemos hacer lo siguiente:

ComprobanteDePago comprobante = new ComprobanteDePago();
Set<ConstraintViolation<ComprobanteDePago>> errors = validator.validate(comprobante);
System.out.println("Errores:"+ errors);

Acá estamos llamando al método “validate” del validator y le estamos enviando la instancia del objeto. De esta manera obtendremos todos los errores luego de que todas las restricciones hayan sido revisadas.

A tener en cuenta

La anotación “@Valid” no es tomada en cuenta cuando se usa cualquiera de los métodos: “validateProperty” o “validateValue”.

JSR 303 – Bean Validation

Thursday, July 1st, 2010 Autor : gmateo

Código Fuente: Ejemplo 1

Esta especificación define el API para poder validar JavaBeans tanto en Java EE como en Java SE.

El objetivo de esta especificación es proveer los mecanismos necesarios para poder declarar y validar Beans, así como también proveer de un repositorio de los constraints y un API para hacer las consultas acerca de los constraints existentes.

Implementaciones

Actualmente existe 2 implementaciones:

- Hibernate Validator, que es la primera implementación y

- Apache Bean Validation tiene soporte para la validación de métodos, lo cual no es parte de la especificación 303 pero es algo que está incluido en los anexos de la misma, lo de malo es que recién esta en su versión: 0.1-incubating.

Para ver los detalles usaremos la implementación: Hibernate Validator.

Declaración de constraints

Los constraints se pueden declarar usando xml o anotaciones, nosotros nos limitaremos a mostrar el uso utilizando anotaciones.

Se pueden dar los siguientes casos:

A nivel de atributo

Sirve para validar que el valor del atributo cumpla ciertos requisitos. Se debe tener en cuenta que el atributo puede tener cualquier nivel de visibilidad (public, prívate, etc), pero no debe ser estático (static).

public class ComprobanteDePago {
    @OnlyLetters
    private String codigoInterno;
    @NotNull
    private ComprobanteTipo tipo;

 

A nivel de getters

Sirve para validar que la propiedad relacionada al “getter” cumpla ciertos requisitos. Al validar se toma en cuenta el valor que retorna el método “getter”.

@Future
public Date getFechaDeEntrega() {
    return fechaDeEntrega;
}

 

A nivel de clase o interface

Sirve para hacer validaciones que incluyan más de un campo, en este caso se tiene la referencia a todos los datos de la instancia. Y se puede validar todas las posibles combinaciones del estado actual de la instancia.

@RucInfoChecker
public class ComprobanteDePago {

 

Validación de “graphs of object”.

Esto nos permite validar objetos de manera recursiva. Para esto se debe usar la anotación @Valid. Veamos el siguiente ejemplo para clarificar la idea:

public class Cliente {
    @NotNull
    private String nombre;
}

public class ComprobanteDePago {
    @Valid
    @NotNull
    private Cliente cliente;
}

Acá podemos ver que la clase “ComprobanteDePago” tiene como atributo la clase “Cliente”, además podemos ver que este atributo tiene 2 anotaciones @Valid, @NotNull, teniendo en cuenta esta configuración al momento de validar la clase: “ComprobanteDePago” también se ejecutará la validación de la clase “Cliente”, por tanto se verificará que el atributo “nombre” sea distinto de null.

Este comportamiento también funciona cuando usamos colecciones, arreglos y en general cualquier campo que implemente “Iterable”. En estos casos se ejecuta la validación de cada uno de los elementos de las colecciones.

Veamos el siguiente ejemplo:

public class ComprobanteDePago {
    @Valid
    @Size(min=1, max=20)
    private List items = new ArrayList();
 
Acá @Valid está siendo usado con una lista (List), esto nos indica que cuando se valide la clase “ComprobanteDePago” se ejecutará también la validación de todos los objetos “Item” que estén en la lista.  

A tener en cuenta:

a) Los constraints puedes ser usados tanto en clases como en interfaces.

b) Los constraints se heredan, tanto si se usa extend o implement.

c) En caso de que se sobreescriba un método que ya tenía constraints al final se juntan los constraints del método sobreescrito y el método actual.