Hibernate Validator Versão 3.1.0GA

As anotações são uma maneira muito conveniente e elegante para especificar validações à um modelo de domínio. Você pode, por exemplo, expressar que a propriedade nunca deve ser nula, que um campo não pode ser vazio, que um atributo deve ter um tamanho mínimo e/ou máximo, entre outras coisas. Essas restrições ou validações, são declarados no bean (modelo ou dominio) através do uso de anotações. Um validador verifica se existem violações de restrição para os campos anotados.

O Hibernate Validator trabalha em dois níveis. O primeiro é capaz de verificar in-memory instancias de uma classe para violações de validações. O segundo poderá aplicar as restrições ao metamodelo do Hibernate e incorporá-los no banco de dados do esquema gerado.

Cada anotação de validação está associada a uma implementação de um validador responsável por verificar a validação na inst ncia de entidade. Você pode executar a operação de verificação de inserções e atualizações feitas pelo Hibernate. O Hibernate Validator não se limita a ser usado com o Hibernate, pode-se usá-lo facilmente em qualquer lugar de sua aplicação, bem como com qualquer provedor de persistência Java.

Ao verificar as inst ncias em tempo de execução, o Hibernate Validator retorna informações sobre violações de restrição em um array de InvalidValues. Entre outras informações, o InvalidValue contém uma mensagem de descrição de erro. As mensagens podem ser descritas tanto na própria anotação ou em um arquivo de propriedades que deve estar no pacote principal da aplicação (src).

1. Um pequeno projeto

Para o desenvolvimento deste tutorial serão necessárias algumas bibliotecas como do Hibernate, do Hibernate Validator e do MySql e mais algumas necessárias para rodar o Hibernate. O download do Hibernate Validator poderá ser feito através do link (http://sourceforge.net/projects/hibernate/files/hibernate-validator/3.1.0.GA/) ou neste pacote que montei (http://www.megaupload.com/?d=BN4JHHHJ) onde se encontram todas as bibliotecas necessárias para o projeto, conforme a Figura 1.

Figura 1 - Bibliotecas do projeto.

Figura 1 – Bibliotecas do projeto.

2. Hibernate SessionFactory

Para o Hibernate funcionar precisamos de uma classe que crie uma fabrica de sessões e também de um arquivo de configuração, o hibernate.cfg.xml. Caso ainda não tenha nenhum conhecimento sobre o Framework Hibernate, de uma lida neste artigo: Utilizando Swing com Hibernate (SessionFactory) e só então, volte para o artigo do Hibernate Validator. A classe HibernateUtil (Listagem 1) cria uma conexão com o banco, a configuração se encontra no arquivo hibernate.cfg.xml (Listagem 2). O banco de dados deve ser criado no MySql manualmente, com o nome validator, usuário root e senha "" (vazio). Caso deseje alterar algum destes atributos, modifique-os no arquivo XML. As tabelas serão criadas na primeira execução do Hibernate, crie apenas o banco de dados.

Listagem 1. Classe HibernateUtil
package com.wp.mballem.tutorial;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {
    private static SessionFactory sessionFactory;

    static {
        try {
            // Create the SessionFactory from hibernate.cfg.xml
            sessionFactory =
                    new Configuration().configure().buildSessionFactory();
        } catch (Throwable ex) {
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static Session getSession() {
        return sessionFactory.openSession();
    }
}
Listagem 2. Arquivo hibernate.cfg.xml
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.dialect">
            org.hibernate.dialect.MySQLDialect
        </property>
        <property name="hibernate.connection.url">
            jdbc:mysql://localhost/validator
        </property>
        <property name="hibernate.connection.driver_class">
            com.mysql.jdbc.Driver
        </property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password"></property>
        <property name="hibernate.format_sql">true</property>
        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.use_sql_comments">false</property>
        <!-- Disable the second-level cache -->
        <property name="hibernate.cache.provider_class">
            org.hibernate.cache.NoCacheProvider
        </property>
        <!-- DB schema will be updated if needed -->
        <property name="hbm2ddl.auto">update</property>
        <property name="hibernate.current_session_context_class">
            thread
        </property>
        <!-- mapeamentos -->
        <mapping class="com.wp.mballem.tutorial.Usuario"/>
        <mapping class="com.wp.mballem.tutorial.Contato"/>
    </session-factory>
</hibernate-configuration>

3. Classe Usuario

Na Listagem 3 temos a classe de domínio/modelo (bean) Usuario. Preste atenção nas classes importadas para não haver erro e importar classes semelhantes de outros pacotes. As anotações referentes ao Hibernate Validator nesta classe serão:

  • @NotNull – Evita que seja inserido no atributo anotado um valor nulo;
  • @Length – Especifica o tamanho mínimo e máximo permitido no atributo;
  • @Pattern – Especifica uma validação através do uso de expressão regular (regex);
  • @Range – Especifica que o valor deve estar entre um mínimo e um máximo;
Listagem 3. Classe Usuario
package com.wp.mballem.tutorial;

import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import org.hibernate.validator.Length;
import org.hibernate.validator.NotNull;
import org.hibernate.validator.Pattern;
import org.hibernate.validator.Range;

import javax.persistence.*;
import java.io.Serializable;
import java.util.Collection;

@Entity
@Table(name = "USUARIOS")
public class Usuario implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID")
    private int id;

    @Column(name = "NOME", unique = true)
    @NotNull(message = "{field.null}")
    @Length(min = 3, max = 20, message = "{length.nome}")
    private String login;

    @Column(name = "SENHA")
    @NotNull(message = "field.null")
    @Length(min = 6, max = 8, message = "deve conter de 6 a 8 caracteres!")
    private String senha;

    @Column(name = "EMAIL")
    @NotNull(message = "{field.null}")
    @Pattern(regex = ".+@.+\\.[a-z]+", message = "{invalid.mail}")
    private String email;

    @Column(name = "IDADE")
    @Range(min = 18, max = 60, message = "{min.max.age}")
    private int idade;

	//gere os métodos get e set
	//gere os métodos equals e hashCode
	//gere o método toString
}

Dentro das anotações são especificados os valores que regem as regras da anotação. Além disso, poderá ser adicionada uma mensagem que será exibida quando a regra do campo não for respeitada. Para inserir uma mensagem há duas maneiras. A primeira é digitar a mensagem diretamente, como por exemplo, no atributo senha. A segunda forma é criar um arquivo do tipo propriedades e inserir as mensagens no arquivo e então, apenas indicar na anotação qual mensagem ela deve exibir, como por exemplo, no atributo login. O arquivo deve ser salvo no diretório raiz da aplicação (src), veja na Figura 2.

Figura 2 - Arquivo de Mensagens

Figura 2 – Arquivo de Mensagens

Já na Listagem 4, é possível conferir como as propriedades devem ser configuradas no arquivo ValidatorMessages.properties. Após então é necessário usar o nome da propriedade para recuperar a mensagem, como no exemplo do campo login. O nome da propriedade deve estar dentro de chaves quando for recuperada e as chaves devem estar dentro de aspas duplas: message = "{length.nome}".

Listagem 4. Arquivo de Mensagens – ValidatorMessages.properties.
length.nome = deve conter entre 3 e 20 caracteres!
field.null = não pode ser nulo!
min.max.age = deve ser entre 18 e 60 anos!
invalid.mail = inválido!

4. Exibindo as Mensagens do Validator

Para que as mensagens sejam exibidas para o usuário é necessário criar um método que faça isso. Neste tutorial foi implementada uma classe chamada ValidatorMessage e nela um método do tipo estático getMessage(). Este método deve receber como par metro o objeto que será validado, veja na Listagem 5. Criar uma classe para esse fim não é um ato obrigatório, você deve encontrar uma melhor forma para usar em seu projeto.

Listagem 5. Classe ValidatorMessage
package com.wp.mballem.tutorial;

import org.hibernate.validator.ClassValidator;
import org.hibernate.validator.InvalidValue;

import java.util.ResourceBundle;

public class ValidatorMessage {
    public static String getMessage(Object obj) {
        ClassValidator validator = new ClassValidator(
                obj.getClass(),
                ResourceBundle.getBundle("ValidatorMessages")
        );

        InvalidValue[] validationMessages = validator.getInvalidValues(obj);
        for (InvalidValue inv : validationMessages) {
            return inv.getPropertyName() + " " + inv.getMessage();
        }
        return null;
    }
}

O método funciona da seguinte forma:

  • O método recebe por parametro o objeto que será testado.
  • Um objeto do tipo ClassValidator é instanciado. Em seu construtor passamos como par metros a classe que contém as anotações de validação e o nome do arquivo que contem as mensagens.
  • Em seguida é criado um array com as mensagens de erro. Através de um loop, recuperamos a mensagem e retornamos uma String com o nome do atributo mais a mensagem a ser exibida.

As mensagens são validadas de cima para baixo. Por exemplo, na Listagem 3 o atributo login possui duas validações, são elas @NotNull e @Length. Sendo assim, o validator vai primeiro testar se o campo é nulo, para depois testar o tamanho da palavra.

5. Testando as validações de Usuario

Agora vamos criar uma classe principal para testar as validações inseridas na classe Usuario. Na Listagem 6 criamos um método principal onde inserimos valore em um objeto do tipo Usuario, em seguida executamos o método inserir() e depois o findAll().

Listagem 6. Classe UsuarioTeste
package com.wp.mballem.tutorial;

import org.hibernate.Session;
import java.util.List;

public class UsuarioTeste {
    public static void main(String[] args) {
        Usuario u = new Usuario();
        u.setEmail("mariana@email.com");
        u.setIdade(10);
        u.setLogin("mariana");
        u.setSenha("mariana");
        inserir(u);
        findAll();
    }

    private static void inserir(Usuario usuario) {
        Session session = HibernateUtil.getSession();
        session.beginTransaction();
        session.getTransaction().begin();
        try {
            session.save(usuario);
            session.getTransaction().commit();
        } catch (Exception e) {
            System.out.println(
                    "Teste.inserir: " + ValidatorMessage.getMessage(usuario)
            );
        } finally {
            session.close();
        }
    }

    public static void findAll() {
        Session session = HibernateUtil.getSession();
        List usuarios = session.createCriteria(Usuario.class).list();
        for (Usuario u : usuarios) {
            System.out.println(u.toString());
        }
    }
}

Nesse exemplo, tentamos inserir uma idade igual a 10 anos, porém, nossa regra de validação exige que a idade seja entre 18 e 60 anos. Quando o Hibernate tentar realizar o insert (save(usuario)) vai gerar uma exceção e imprimir a seguinte linha: Teste.inserir: idade deve ser entre 18 e 60 anos!. Note que dentro do catch() {//aqui}, temos uma chamada ao método getMessage() e passamos como parametro o objeto u (de Usuario). Agora altere a idade para um valor valido e então verá que o insert será realizado e logo depois a linha inserida será exibida no console. Tente também alterar os demais campo com situações irregulares e veja a exibição das mensagens do validator.

6. Classe Contato

Vamos inserir outra classe no projeto, a classe Contato. Esta classe terá um relacionamento com Usuario, onde cada usuário (1) terá vários (N) contatos. Confira na Listagem 7 o código da classe Contato. A anotação @Valid no atributo usuario vai fazer com que quando um objeto Contato for inserido com um Usuario a validação de usuario também seja analisada pelo Hibernate Validator. Caso essa anotação não seja inserida, a validação de Usuario será testada mas a mensagem de erro não será exibida. No campo fone a validação é realizada através de expressão regular e o seguinte formato é exigido: (xx)xxxx-xxxx

Listagem 7. Classe Contato
package com.wp.mballem.tutorial;

import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.hibernate.validator.Length;
import org.hibernate.validator.NotNull;
import org.hibernate.validator.Pattern;
import org.hibernate.validator.Valid;

import javax.persistence.*;
import java.io.Serializable;

@Entity
@Table(name = "CONTATOS")
public class Contato implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID")
    private Long id;

    @Column(name = "NOME", unique = true)
    @NotNull(message = "{field.null}")
    @Length(min = 3, max = 20, message = "{length.nome}")
    private String nome;

    @Column(name = "FONE")
    @Pattern(regex = "^\\(\\d{2}\\)\\d{4}-\\d{4}$",
            message = "Telefone inválido!"
    )
    private String fone;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "ID_USUARIO", insertable = true, updatable = true)
    @Fetch(FetchMode.JOIN)
    @Cascade(CascadeType.SAVE_UPDATE)
    @Valid
    private Usuario usuario;
         //gere os métodos get e set
         //gere os métodos equals e hashCode
         //gere o método toString
}

//Insira na classe Usuario o seguinte import e atributo:

    import java.util.Collection;

    @OneToMany(mappedBy="usuario", fetch = FetchType.LAZY)
    @Cascade(CascadeType.ALL)
    private Collection contatos;

7. Classe ContatoTeste

Vamos criar uma classe de teste para inserir um contato. Cada contato inserido terá também um novo usuário cadastrado no banco de dados, veja um exemplo na Listagem 8. Quando o código for executado, a seguinte mensagem de erro será exibida: Teste.inserir: email inválido!. Veja que o campo email não possui o formato exigido. Caso a anotação @Valid não fosse usada na classe Contato, a mensagem de erro neste caso seria essa: Teste.inserir: null.

Listagem 8. Classe UsuarioTeste
package com.wp.mballem.tutorial;

import org.hibernate.Session;
import java.util.List;

public class ContatoTeste {
    public static void main(String[] args) {
        Usuario u = new Usuario();
        u.setEmail("carlinha@email");
        u.setIdade(25);
        u.setLogin("carlinha");
        u.setSenha("carlinha");

        Contato c = new Contato();
        c.setNome("Joao Mario");
        c.setFone("(55)3000-1205");
        c.setUsuario(u);

        inserir(c);
        findAllContatos();
    }

    private static void inserir(Contato contato) {
        Session session = HibernateUtil.getSession();
        session.beginTransaction();
        session.getTransaction().begin();
        try {
            session.save(contato);
            session.getTransaction().commit();
        } catch (Exception e) {
            System.out.println(
                    "Teste.inserir: " + ValidatorMessage.getMessage(contato)
            );
        } finally {
            session.close();
        }
    }

    public static void findAll() {
        Session session = HibernateUtil.getSession();
        List contatos = session.createCriteria(Contato.class).list();
        for (Contato c : contatos) {
            System.out.println(c.toString());
        }
    }
}

8. Anotações do Hibernate Validator

Veja na Tabela 1 todas as anotações disponíveis na versão 3.1.0GA do Hibernate Validator.

Tabela 1 ? Anotações disponíveis no Hibernate Validator 3.1.0GA.

Tabela 1 – Anotações disponíveis no Hibernate Validator 3.1.0GA.

Saiba mais

Ballem

Marcio Ballem é bacharel em Sistemas de Informação pelo Centro Universitário Franciscano em Santa Maria/RS. Tem experiência com desenvolvimento Delphi e Java em projetos para gestão pública e acadêmica. Possui certificação em Java, OCJP 6.

Você pode gostar...