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.
Programação, Mobile, Front-end, Design & UX, Infraestrutura e Business
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.
Gostei desde o inicio dessa proposta do Bean Validation [JSR 303], ótimo post!
É que o intuito era mesmo mostrar como fazer sua própria anotação. Mas é perfeitamente válida sua idéia Michael.
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”.
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.Saquei
Exato, Paulo.
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
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.
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.
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?
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
Olá Caelum, tudo bem contigo?
Gostaria de saber aonde encontro um guia de referencia com todas as Anotações ja definidas?
Abraços
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
Fábio, segue um exemplo:
http://download.oracle.com/javaee/6/tutorial/doc/gkfgx.html
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
acho que as anotações não apareceram….
substitua # por
#/f:validateBean>
sei q é necessario colocar essas anotações, mas alguem sabe o que é cada uma, so de curiosidade?
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
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!
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.
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?
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?
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.