Java EE 6: Começando com Bean Validation

Postado em 03. fev, 2010 por Lucas Souza em Java

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.

Lucas Souza

Tags: , , , , , , , , ,

20 Respostas para “Java EE 6: Começando com Bean Validation”

  1. 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. fev, 2010

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

  3. Lucas Souza

    04. fev, 2010

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

  4. Rodrigo

    04. fev, 2010

    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. fev, 2010

    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. fev, 2010

    Saquei

  7. Exato, Paulo.

  8. Daniel Braga

    05. fev, 2010

    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. fev, 2010

    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. fev, 2010

    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. mar, 2010

    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. jul, 2010

    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. mai, 2011

    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. ago, 2011

    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. out, 2011

  16. Otavio A. Junior

    28. fev, 2013

    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. fev, 2013

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

    substitua # por
    #/f:validateBean>

  18. carol

    29. abr, 2013

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

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

Trackbacks/Pingbacks

  1. TOP 10: Os melhores posts de 2010 da Caelum | blog.caelum.com.br - fevereiro 10, 2011

    [...] Java EE 6: Começando com Bean Validationpor Lucas Souza, em 03/02 [...]

  2. JavaBeans Validation – Bean Validation | Marcelo Guará - junho 9, 2011

    [...] Caelum já postou sobre essa api e vale a pena conferir o artigo em que falam sobre isso mas existem alguns, digamos, [...]

Deixar uma Resposta