Java EE 6: Começando com Bean Validation

Não existe tarefa mais comum hoje em dia do que validar dados em uma aplicação. Por exemplo, validamos se na camada de apresentação o usuário preencheu algum campo obrigatório, depois fazemos a mesma lógica de validação em nossa regra de negócio e por último validamos se os dados que serão salvos no banco também estão corretos. O que na maioria das vezes os desenvolvedores fazem é validar estas regras em todos os lugares, e muitas vezes resulta em validações complicadas e possíveis erros na aplicação. Muitos desenvolvedores consideram essa tarefa enfadonha.

Na nova versão do Java EE lançada dezembro de 2009 existem grandes novidades: Servlets 3.0, JAX-RS, CDI, JSF 2.0 e a Bean Validation, definida pela JSR 303. Com a Bean Validation é possível realizarmos validações atráves de metadados (anotações) e as utilizarmos em todas as camadas de nossa aplicação.

Podemos até mesmo criar nossas próprias regras de validações e aplicarmos em outros pontos do nosso sistema. Essas regras podem ser facilmente integradas com JPA e JSF, por exemplo. Vamos considerar uma classe que represente um contato:

public class Contato {

  private String nome;

  private String email;

  // getters e setters

  public void enviaEmail() {
    // codigo envio de email
  }
}

E vamos adicionar nossas regras de validação utilizando a Bean Validation:

public class Contato {

  @NotEmpty
  @Pattern(regexp = ".+@.+\\.[a-z]+")
  private String email;
   
  @NotEmpty
  private String nome;

  // getters e setters

  public void enviaEmail() {
  // codigo envio de email
  }
}

A anotação @NotEmpty garante que o email não poderá ser vazio assim como o atributo nome. No atributo email também colocamos a anotação @Pattern, onde podemos passar qualquer expressão regular, que o Bean Validation validará o valor passado ao atributo nome com a expressão regular.

Lembrando que o Bean Validation é uma especificação e como toda especificação, para utilizarmos precisamos de uma implementação para a utilizarmos. A implementação usada nos exemplos é a Hibernate Validator, e é a implementação de referência.

Integrando com o JSF

O código abaixo mostra um formulário simples utilizando JSF, que chama um método enviaEmail do ManagedBean quando clicamos no botão “EnviaEmail“:

<h:form id="formulario">
     <h:panelGrid columns="3">
          <h:inputText id="email" value="#{contato.email}"/>
          <h:message for="email" styleClass="error"/>
          <h:commandButton action="#{contato.enviaEmail}"
               value="Enviar Email"/>
     </h:panelGrid>
</h:form>

Precisamos ainda anotar nossa classe Contato com @ManagedBean(name="contato") e @RequestScoped para que a mesma seja um ManageBean e consiga integrar-se com o JSF.
Quando clicarmos no botão “Enviar Email” automaticamente será validado se o atributo email foi preenchido a atende a expressão regular da anotação @Pattern.

Podemos muitas vezes nos deparar com algum caso onde é necessário fazermos uma validação um pouco mais específica, algo que não esteja implementado por padrão no Bean Validation. Validar um CEP seria um bom exemplo. O primeiro passo para fazermos nossa própria validação é criar uma anotação que sugira o nome desta validação.

@Constraint(validatedBy = CepValidator.class)
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Cep {
  
  String message() default "Cep inválido";
  Class<?>[] groups() default { };
  Class<? extends Payload>[] payload() default { };

}

Reparem que em nenhum momento fizemos de fato a validação se o CEP é válido ou não. Por isso anotamos a anotação CEP que acabamos de criar com @Constraint e setamos o atributo validatedBy com a classe que está nossa validação realmente.
O próximo passo é criarmos a classe CEPValidator que fará a validação. Esta classe tem que obrigatóriamente implementar a interface javax.validation.ConstraintValidator:

public class CepValidator implements 
            ConstraintValidator<Cep, String> {

  private Pattern pattern = 
          Pattern.compile("[0-9]{5}-[0-9]{3}");

  @Override
  public void initialize(Cep constraintAnnotation) {

  }

  @Override
  public boolean isValid(String value, 
           ConstraintValidatorContext context) {
    Matcher m = pattern.matcher(value);
    return m.matches();
  }

}

No método isValid verificamos e retornamos um booleano dizendo se a String passada como argumento está de acordo com a expressão regular que criamos no topo da classe. Podemos agora validar qualquer atributo de algum Java Bean, devendo apenas anotar o atributo com a nossa recém criada @CEP. Por exemplo:

public class Contato {

  @Cep
  private String cep;

  // getters e setters
}

Para rodar esse exemplo precisamos de um servidor de aplicação compatível com o Java EE 6, como o Glassfish 3.0. A premissa do Bean Validation é que muitos outros frameworks acabem por adotar um único mecanismo de validação, sendo possível reaproveitar o código criado aqui. O Caelum Stella, que fornece diversos validadores para sistemas que envolvem o domínio brasileiro, em breve será compatível com essa especificação, possibilitando que você utilize-o em todas as diversas especificações que compões o Java EE.

Além disso precisamos de um Servidor de Aplicação compatível com a versão 6 do Java EE. Por enquanto o único servidor que implementa essa versão é o Glassfish v3.0 que por sinal também é uma implementação de referência.

26 Comentários

  1. Michael Nascimento Santos 04/02/2010 at 11:49 #

    No caso desse exemplo, seria mais interessante anotar @Cep com @Pattern do que implementar algo que já existe na mão. Inclusive isso mostra o poder da composição das regras na API.

  2. Leandro 04/02/2010 at 13:14 #

    Gostei desde o inicio dessa proposta do Bean Validation [JSR 303], ótimo post!

  3. Lucas Souza 04/02/2010 at 13:41 #

    É que o intuito era mesmo mostrar como fazer sua própria anotação. Mas é perfeitamente válida sua idéia Michael.

  4. Rodrigo 04/02/2010 at 18:35 #

    Discordo do Michael, é muito mais claro você criar uma anotações como @Cep nesses casos do que espalhar Regex complicados pelo código.
    Além disso foi um exemplo para “explicar o poder de Extensão das regras na API”.

  5. Paulo Silveira 04/02/2010 at 18:41 #

    Oi Rodrigo. O Michael não quis dizer para usar a anotação @Pattern(regexp ="") em cima de cada atributo, o que realmente não seria limpo. Ele quis dizer para usar o @Pattern em cima da @interface Cep, ai nem haveria necessidade de criar uma implementação de ConstraintValidator, já que o Beans Validation permite essa sobreposição de anotações, permitindo você compor regras de maneira fácil.

  6. Rodrigo 05/02/2010 at 10:02 #

    Saquei

  7. Michael Nascimento Santos 05/02/2010 at 10:25 #

    Exato, Paulo.

  8. Daniel Braga 05/02/2010 at 13:45 #

    Acredito que a versão “JBoss AS 6.0.0.M1” implemente algumas das características do JEE 6, como “(…) CDI/Weld, BV and JSF2”.

    Ref: http://www.jboss.org/feeds/post/jboss_as_6_0_0_m1

  9. Henrique Rocha 10/02/2010 at 16:05 #

    Lucas Souza e integrantes deste blog, gostaria de saber se vocês tiveram alguma problema na instalação do JEE 6 SDK. Pois não consegui instalar, várias outras pessoas no Brasil tiveram ou ainda tem problema de instalação.
    O comportamento é que baixei o arquivo: java_ee_sdk-6-windows.exe, executei e depois que mostrou a tela de instalação, após uns 3 a 5 segundos a tela sumiu e não acontece mais nada.

  10. Lucas Souza 22/02/2010 at 17:58 #

    Olá Henrique, eu apenas baixei o GlassFish v3 no link: https://cds.sun.com/is-bin/INTERSHOP.enfinity/WFS/CDS-CDS_Developer-Site/en_US/-/USD/ViewProductDetail-Start?ProductRef=sges-3-oth-JPR@CDS-CDS_Developer

    E instalei dentro do Eclipse. Tudo funcionou normalmente.

  11. Ronildo Braga Junior 03/03/2010 at 11:18 #

    Bom dia.

    Excelente artigo, explica muito bem como funciona a proposta da JSR 303.

    Mas tenho uma dúvida. É possível fazer esta validação usando richfaces? Por exemplo:

    Creio que dessa forma a requisição não será validada, existe uma solução?

  12. Erick 08/07/2010 at 14:18 #

    Pessoa é o seguinte, estou tentando fazer uma Constraint de Ie, porem ela deve validar de acordo com o estado

    Algem sabe fazer isto funcionar

    vejo o codigo

    A Constraint
    view plaincopy to clipboardprint?

    1. @Constraint(validatedBy = IeValidacao.class)
    2. @Documented
    3. @Target(ElementType.FIELD)
    4. @Retention(RetentionPolicy.RUNTIME)
    5. public @interface Ie {
    6.
    7. String message() default “Cep inválido”;
    8. Class[] groups() default { };
    9. Class[] payload() default { };
    10. String estado() default “Sp”;
    11. }

    @Constraint(validatedBy = IeValidacao.class) @Documented @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Ie { String message() default “Cep inválido”; Class[] groups() default { }; Class[] payload() default { }; String estado() default “Sp”; }

    A classe de validação
    view plaincopy to clipboardprint?

    1. public class IeValidacao implements ConstraintValidator {
    2.
    3. private String estado;
    4.
    5. public void initialize(Ie constraintAnnotation) {
    6. estado = constraintAnnotation.estado(); // não esta ENTTRANDO neste metodo
    7. }
    8.
    9. public boolean isValid(String value, ConstraintValidatorContext context) {
    10. if (estado != null && estado == “Sp” && value == “123”) {
    11. return true;
    12. }
    13. if (estado != null && estado == “Mg” && value == “456”) {
    14. return true;
    15. }
    16. return false;
    17. }
    18. }

    public class IeValidacao implements ConstraintValidator { private String estado; public void initialize(Ie constraintAnnotation) { estado = constraintAnnotation.estado(); // não esta ENTTRANDO neste metodo } public boolean isValid(String value, ConstraintValidatorContext context) { if (estado != null && estado == “Sp” && value == “123”) { return true; } if (estado != null && estado == “Mg” && value == “456”) { return true; } return false; } }

    Classe Pessoa
    view plaincopy to clipboardprint?

    1. public class Pessoa {
    2. private String estado ;
    3.
    4. @Ie(estado= estado ) // ERRO NESTA LINHA, axo que so permite constante, como faze então?
    5. private String ie;
    6.
    7.
    8. public String getIe() {
    9.
    10. return ie;
    11. }
    12.
    13. public void setIe(String ie) {
    14. this.ie = ie;
    15. }
    16.
    17. public String getEstado() {
    18. return estado;
    19. }
    20.
    21. public void setEstado(String estado) {
    22. this.estado = estado;
    23. }
    24.
    25.
    26.
    27. }

    public class Pessoa { private String estado ; @Ie(estado= estado ) // ERRO NESTA LINHA, axo que so permite constante, como faze então? private String ie; public String getIe() { return ie; } public void setIe(String ie) { this.ie = ie; } public String getEstado() { return estado; } public void setEstado(String estado) { this.estado = estado; } }

    Pessoa observe que comentei as linha que estou com dificuldade, mais seria mais ou menos isso. alguem poderia me ajudar

    Deste ja agradeço

  13. João de Lima 19/05/2011 at 14:15 #

    Olá Caelum, tudo bem contigo?

    Gostaria de saber aonde encontro um guia de referencia com todas as Anotações ja definidas?

    Abraços

  14. fabio 08/08/2011 at 10:44 #

    Estou tentando fazer a Annotation de validação igual ao Michael falou, porem nao consegui, tentei o seguinte:

    @Constraint(validatedBy = Pattern.class)
    @Documented
    @Pattern(regexp=”(0?[1-9]|[12][0-9]|3[01])/(0?[1-9]|1[012])/((19|20)\\d\\d)”)
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ValidaData {
    String message() default “Data inválida”;
    Class[] groups() default {};
    Class[] payload() default{};
    }

    porem nao sei o que colocar no @Constraint para nao dar erro.. alguem pode me ajudar??

    obrigado

  15. Gustavo Lobato 13/10/2011 at 14:51 #

    Fábio, segue um exemplo:
    http://download.oracle.com/javaee/6/tutorial/doc/gkfgx.html

  16. Otavio A. Junior 28/02/2013 at 23:20 #

    Estava na tentativa de implementar e não estava dando certo… Dei uma pesquisada, deve-se adicionar as validações dentro da tag #campos a validar #

    att

  17. Otavio A. Junior 28/02/2013 at 23:22 #

    acho que as anotações não apareceram….

    substitua # por
    #/f:validateBean>

  18. carol 29/04/2013 at 11:12 #

    sei q é necessario colocar essas anotações, mas alguem sabe o que é cada uma, so de curiosidade?

    @Documented
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)

  19. Victor Maehira 26/08/2014 at 12:05 #

    Ainda nao estou usando Bean Validation, mas vai me quebra um galhao. Muitas vezes as regras estao espalhadas no Java e feitas “na mao”. E, ainda tenho regras de validacao redundantes no Javascript. Tem alguma sugestao para resolver isso?
    Valeu!

  20. Heitor Neto 23/09/2014 at 08:04 #

    Bom dia!

    Estou estudando bean validation, porem, não estou conseguindo encontrar documentos/artigo sobre o assunto que não utilize JSF, todo material que encontro faz referência ao JSF. Alguem sabé como posso fazer o uso do bean validatiom com Angular, por exemplo.

  21. Jeremias 05/07/2016 at 13:15 #

    Estou uma duvida se alguém puder responder agradeço.

    Tenho uma tela que esta passando por este processo de validação como citado acima, porem a tela e cadastro e pesquisa e gostaria de validar a mesmo somente quando a ação de gravação fosse executada e quando fosse pesquisa os campos aceitasse notnull.
    Qual a melhor forma de implementar isto?

  22. Tiago 14/09/2016 at 17:12 #

    Já venho usando a abordagem de beans validations, porém sempre tive uma dúvida que nunca consegui sanar. E quando a validação for condicional? Ou seja em uma determinada operação preciso que seja executado @ValidarAlgo e em outra operação preciso que seja executado @ValidarOutroAlgo.
    Hoje trato com uma camada TO com diferentes classes cada uma com a validação específica. Mas e se fosse usar a mesma classe, tem como?

  23. Thiago Moreno 21/02/2017 at 17:53 #

    O bean validation so funciona com os atributos de uma classe? Caso eu queria validar os atributos de um outro objeto que é atributo da classe? por exemplo, Pessoa e Endereco? Quando eu salvo os atributos da pessoa sao validados porem os attributos de endereco nao passam pela validacao.

Deixe uma resposta