O Hibernate é uma implementação do padrão Java Persistence API (JPA). Existem soluções em que usar um ORM (Object-Relational Mapping) como Hibernate é apropriado, no entanto, algumas vezes é mais apropriado usarmos o tradicional JDBC.

Abaixo ilustraremos alguns exemplos onde é mais apropriado o uso de Hibernate e contrastaremos com o tradicional JDBC. Também veremos exemplos de como utilizar Hibernate.

Plain Old Java Objects (POJOs)

No mundo ideal da programação seria maravilhoso se tivéssemos um objeto Java simples sem nenhuma dependência com bibliotecas externas e pudéssemos armazenar este objeto na nossa Base de Dados sem nenhum esforço e nenhum problema de performance, como ilustrado abaixo:


ObjetoJavaPOJO pojo = new ObjetoJavaPOJO();
FrameworkORM orm = FrameworkORM.getInstance();
orm.save(pojo);

O Hibernate faz exatamente isso, no entanto, como nem tudo é perfeito temos que criar e configurar alguns arquivos de configuração e considerar algumas questões de performance. Dessa forma, o Hibernate permite que possamos armazenar POJOs diretamente na nossa Base de Dados.

Um termo comumente usado é o de Mapeamento Objeto-relacional, isto é, o mapeamento de objetos em Java diretamente para entidades relacionais na base de dados.

Origens do Hibernate e Mapeamento Objeto-Relacional

Quando usamos JDBC temos um considerável número de códigos e várias regras que devem ser cuidadosamente observadas para assegurar que a nossa aplicação não está desperdiçando recursos.

Segue abaixo um exemplo de uma aplicação usando JDBC para listar os tipos de assunto de alguns artigos de uma base de dados.


package br.com.artigos.dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.faces.bean.ManagedBean;
import br.com..artigos.TpAssuntoVO;

public class TipoAssuntoDAO {
 
	public TpAssuntoVO consulta(Integer nroIntTpAssunto) {
	    
	    TpAssuntoVO tpAssuntoVO = null;
	    Connection con = null;
	    Statement st = null;
	    ResultSet rs = null;
	
	    String url = "jdbc:postgresql://pgsql01.redehost.com.br:5432/bd";
	    String user = "userdefault";
	    String password = "passwddefault";
	
	    try {
	    	 Class.forName("org.postgresql.Driver");
	        con = DriverManager.getConnection(url, user, password);
	        st = con.createStatement();
	        rs = st.executeQuery("select \"NRO_INT_TP_ASSUNTO\", \"ASSUNTO\" from public.tipoassunto " +
	        		" where \"NRO_INT_TP_ASSUNTO\" = " + nroIntTpAssunto);

	        if (rs.next()) {
	        	tpAssuntoVO = new TpAssuntoVO();
	        	tpAssuntoVO.setNroIntTpAssunto(rs.getInt("NRO_INT_TP_ASSUNTO"));
	        	tpAssuntoVO.setNome(rs.getString("ASSUNTO"));
	        }
	    } catch (SQLException ex) {
	    	System.out.println("Problem: " + ex.toString());
	    } catch (Exception ex) {
	    	System.out.println("Problem: " + ex.toString());
	    } finally {
	        try {
	            if (rs != null) {
	                rs.close();
	            }
	            if (st != null) {
	                st.close();
	            }
	            if (con != null) {
	                con.close();
	            }
	
	        } catch (SQLException ex) {
	        }
	    }
	        
	    return tpAssuntoVO;
	}

}
Listagem 1. Exemplo de Aplicação usando JDBC

Podemos notar que essa técnica manual possui diversas situações que devem ser gerenciadas pelo desenvolvedor, e a chance de ocorrerem problemas é realmente muito grande. Existem algumas técnicas para diminuir a chance de erro e o grande número de códigos necessários, mas mesmo assim muitas coisas ainda precisam ser gerenciadas manualmente e quanto maior fica o código maior é a chance de ocorrerem problemas.

Esse é um momento oportuno para relatar o quanto essa técnica manual usando diretamente JDBC é problemática. Na minha empresa cada vez que um novo programador entra na equipe temos problemas com as conexões, isso ocorre porque infelizmente o novo programador não dedicou total atenção para algumas situações que usar JDBC diretamente requer, e às vezes esquecer de fechar alguma conexão ou esquecer de um finally, ou caso aconteça um erro inesperado e ele não seja tratado corretamente podem causar até mesmo um estouro na pilha de conexões levando o servidor de aplicação a ficar inoperante. Muitas vezes até os programadores mais experientes esquecem de detalhes que acabaram levando ao caos no dia seguinte. Mas felizmente hoje temos alternativas que nos permitem salvar muitas dores de cabeça.

Usando Hibernate Para Solucionar os Problemas

O Hibernate soluciona grande parte desses pontos ou no mínimo alivia alguns desses problemas.

Com Hibernate podemos armazenar POJOs diretamente nas bases de dados e definir relacionamentos entre as classes, representando o Banco de dados.

Uma vantagem do Hibernate é que ele não tem requisitos para servidores de aplicação J2EE ou qualquer outro ambiente. Portanto, Hibernate pode ser utilizado para aplicações de linha de comandos, aplicações do lado cliente, e outros ambientes de desenvolvimento onde um servidor J2EE não está imediatamente disponível.

A vantagem do Hibernate é que ele usa POJOs que podem ser facilmente portáveis para qualquer aplicação. Não há qualquer dependência entre os POJOs e o Hibernate, portanto os POJOs ainda podem ser utilizados para qualquer outra coisa que não requeira persistência. Dessa forma, usar Hibernate é uma escolha natural para sobrepor métodos ad hoc.

Abaixo segue um exemplo de como recuperar os tipos de assuntos da base de dados, o mesmo que foi feito anteriormente, mas agora utilizando Hibernate:


public static List getTpAssunto(int nroIntTpAssunto) throws MessageException
{
	SessionFactory sessions = new AnnotationConfiguration().configure().buildSessionFactory();
	Session session = sessions.openSession();
	
	Transaction tx = null;
	
	try {
		tx = session.beginTransaction();
		List list = session.createQuery("from TpAssuntoVO").list();
		tx.commit();
		tx = null;
		return list;
	} catch ( HibernateException e ) {
		if ( tx != null ) tx.rollback();
		throw new MotdException("Failed to retrieve message from the database.",e);
	} finally {
		session.close();
	}
}
Listagem 2. Recuperando tipos de assuntos da base de dados

Podemos notar inicialmente a quantidade de código a menos que temos em relação ao código anterior utilizando JDBC. Também não temos nenhum tipo de configuração em relação a Base de Dados, tudo está separado num arquivo de configuração e não temos que se preocupar em converter resultados da base de dados para o nosso objeto Java.

Mapeamento

O Hibernate precisa de alguma forma dizer quais tabelas estão relacionadas com quais objetos. Isto é chamado de mapeamento. Mapeamentos podem ou ser providenciados através de anotações Java, ou através de um arquivo de mapeamento em XML. As nossas classes Java POJO serão anotadas diretamente. A maioria dos desenvolvedores preferem anotar diretamente as classes, pois isso dá um visão mais clara do que estamos tentando fazer com a classe.

Segue abaixo um exemplo de uma classe Java POJO anotada:


package entidades;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="usuario_bd")
public class Usuario {
	
	@Id
	Integer nroIntUsuario;
	String nome;
	String email;
	String usuario;
	String senha;

	
	
	public Usuario(String nome, String email, String usuario, String senha) {
		this.nome = nome;
		this.email = email;
		this.usuario = usuario;
		this.senha = senha;
	}
	
	public Usuario() {
	}

	
	

	public Integer getNroIntUsuario() {
		return nroIntUsuario;
	}

	public void setNroIntUsuario(Integer nroIntUsuario) {
		this.nroIntUsuario = nroIntUsuario;
	}

	public String getNome() {
		return nome;
	}

	public void setNome(String nome) {
		this.nome = nome;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getUsuario() {
		return usuario;
	}

	public void setUsuario(String usuario) {
		this.usuario = usuario;
	}

	public String getSenha() {
		return senha;
	}

	public void setSenha(String senha) {
		this.senha = senha;
	}
	
}
Listagem 3. Classe Java POJO Anotada

No exemplo acima temos uma classe POJO bem simples que já demonstra alguns mapeamentos e características necessárias para o Hibernate poder armazenar ou manipular essa classe. Podemos notar que nesse exemplo temos a anotação @Entity que diz que essa classe é uma entidade, @Table que identifica o nome da tabela que será mapeada, @Id que identifica a chave primária da tabela.

Também podemos notar a presença de um construtor default que é obrigatório. Muitas outras anotações e configurações são possíveis como veremos nos próximos artigos.

Conclusão

Neste artigo estudamos o que é o Hibernate, quais são algumas de suas características e quais as suas vantagens em relação a outros métodos tradicionais como JDBC. Também vimos alguns exemplos práticos de como usar Hibernate e manipular classes POJOs.

Bibliografia:
  • Jeff Linwood and Dave Minter. An introduction to persistence using Hibernate 3.5, Second Edition. Apress.
  • Steve Perkins. Hibernate Search by Example: Explore the Hibernate Search system and use its extraordinary search features in your own applications. Packt Publishing.