O Hibernate facilitou o armazenamento e a recuperação de objetos Java via Mapeamento Objeto-Relacional (Object-Relational Mapping) utilizando arquivos (XML) ou anotações Java. O Hibernate é atualmente uma grande coleção de projetos relacionados permitindo que os desenvolvedores utilizem funcionalidade muito além do que apenas o Mapeamento Objeto-Relacional.

Hibernate é um framework de persistência escrito em linguagem Java, mas também está disponível em .Net com o nome NHibernate. O Hibernate é um software livre de código aberto distribuído com a licença LGPL.

Podemos utilizar o Hibernate em uma aplicação Swing, Servlet, Portlet, JPS ou qualquer outra aplicação Java que tenha acesso a uma Base de Dados. Tipicamente o Hibernate é utilizado ou para criar uma camada de acesso a dados ou para sobrepor uma camada de acesso a dados.

Entre as diversas possibilidades que o Hibernate nos oferece temos as sessões que são muito utilizadas e devem ser bem entendidas pelos desenvolvedores. No restante do artigo explicaremos melhor o que são as sessões, quais são todas as suas funcionalidades disponíveis e como podemos utilizar as suas principais funções nas aplicações.

Hibernate Session

Um objeto de sessão (Session) é usado para criar novas entidades de banco de dados, ler objetos populados da base de dados, atualizar objetos na base de dados e deletar objetos da base de dados.

Sessões também permitem gerenciar transações e obter conexões da API JDBC.

Para aqueles que estão familiarizados com a API JDBC, os objetos Session é como uma conexão JDBC e a SessionFactory, que provê os objetos Session, são como um ConnectionPool do JDBC, na qual provê objetos de conexão. A figura abaixo mostra as familiaridades que existem entre Hibernate e a API JDBC.

Comparação entre JDBC e Hibernate.

Figura 1: Comparação entre JDBC e Hibernate.

Conforme discutimos vagamente acima, uma sessão é sempre criada através de uma SessionFactory que é considerado um objeto pesado (heavyweight), e por isso sempre criamos uma simples instância de um SessionFactory por aplicação. Devemos atentar para o fato que duplicar uma SessionFactory poderemos ter problemas rapidamente e cria-los aumentará significativamente o tempo de processamento. Dessa forma, o ideal é termos uma única SessionFactory para cada base de dados que a nossa aplicação acessar. Objetos SessionFactory são threadsafe, por tanto não é necessário obter uma SessionFactory para cada thread. No entanto, criaremos inúmeros objetos Session, pelo menos um para cada thread usando o Hibernate. Os objetos Session no Hibernate não são threadsafe, portanto compartilhar objetos Session entre thread poderão causar perda de dados ou deadlocks. De fato, criaremos múltiplas instâncias de sessões durante o ciclo de vida de uma thread específica.

Uma SessionFactory é criada por um objeto Configuration, na qual adquire uma informação de configuração do Hibernate e usa isto para gerar uma instância de SessionFactory apropriada. Essa informação de configuração é um arquivo.

Para ler um objeto da base de dados, devemos usar uma sessão direta ou indiretamente. Um exemplo de uso de uma sessão “diretamente” é chamar o método session.get() ou então criar um objeto Query da sessão (Query é muito semelhante a PreparedStatement). Por outro lado, o uso indireto de uma sessão seria usar um objeto associado com a sessão. Um exemplo de uso indireto seria chamar um objeto que não foi ainda carregado e pertence a um objeto que já foi carregado diretamente. Nesse caso temos um lazy loading, onde um objeto não foi inicialmente carregado, mas pode ser carregado posteriormente caso seja necessário. Dessa forma, estamos usando uma sessão indiretamente.

A sessão também possui outras funções, como prover funcionalidades de cache, gerenciar objetos não carregados, além de monitorar alterações em objetos associados (onde as alterações podem ser persistidas na base de dados).

A interface de uma Session possui diversos métodos disponíveis. Na tabela abaixo é mostrado um breve resumo das várias categorias de métodos disponíveis:

A interface de uma Session possui diversos métodos disponíveis. Na tabela abaixo é mostrado um breve resumo das várias categorias de métodos disponíveis:

Método Descrição
save() Salva um objeto na base de dados. Este método não deveria ser chamado por um objeto que foi salvo na base de dados.
saveOrUpdate() Salva um objeto na base de dados ou atualiza a base de dados se o objeto já existe. Este método é menos eficiente que o método save(), pois ele necessidade fazer um SELECT para checar se o objeto já existe, mas não falhará se o objeto já foi salvo.
merge() Mescla os campos de um objeto não persistente dentro de um objeto persistente apropriado (determinado pelo ID). Se tal objeto não existe na base de dados, então um é criado e salvo.
persist() Reassocia um objeto com a sessão, dessa forma as alterações feitas no objeto serão persistidas.
get() Recupera um objeto específico da base de dados pelo objeto identificador.
getEntityName() Retorna o nome da entidade.
getIdentifier() Determina o identificador para um objeto específico associado com a sessão.
load() Carrega um objeto da base de dados pelo identificador do objeto (deveremos usar get() se estivermos certo que o objeto está na base de dados).
refresh() Atualiza o estado de um objeto associado da base de dados.
update() Atualiza a base de dados com alterações de um objeto.
delete() Deleta um objeto da base de dados.
createFilter() Cria um filtro (consulta) para restringir operações na base de dados.
enableFilter() Permite um filtro nomeado nas consultas produzidas pelo createFilter().
disableFilter() Disabilita um filtro nomeado.
getEnabledFilter() Retorna um objeto filtro habilitado atualmente.
createQuery() Cria uma consulta no Hibernate para ser aplicado na base de dados.
getNamedQuery() Retorna uma consulta de um arquivo de mapeamento.
cancelQuery() Cancela a execução de qualquer consulta atualmente em progresso de outra thread.
createCriteria() Cria um objeto Criteria para operações de consulta.
beginTransaction() Inicia uma transação.
getTransaction() Retorna o objeto de transação atual. Este método não retorna null quando não há transações em progresso, o método retorna uma propriedade do objeto retornado como false.
lock() Recebe um bloqueio da base de dados para um objeto.
contains() Determina se um objeto específico está associado com a base de dados.
clear() Limpa a sessão de todas as instâncias carregadas e cancela qualquer salvamento, atualização ou deleção que não tenha sido completado.
evict() Desassocia um objeto da sessão para que as alterações posteriores a ele não sejam persistentes.
flush() Libera todas as alterações pendentes no banco de dados - todos salvamentos, atualizações e exclusões serão realizados. Essencialmente, este método sincroniza a sessão com o banco de dados.
isOpen() Determina se a sessão foi fechada.
isDirty() Determina se a sessão está sincronizada com a base de dados.
getCacheMode() Determina o modo de cache atualmente empregado.
setCacheMode() Altera o modo de cache atualmente empregado.
getCurrentLockMode() Determina o modo de bloqueio atualmente empregado.
setFlushMode() Determina o modo de liberação atualmente usado. As opções são liberar depois de todos as operações, liberar quando necessário, nunca liberar, ou liberar apenas no commit.
setReadOnly() Marca um objeto persistente como apenas leitura (ou como de escrita). Existe menores benefícios de performance marcando um objeto como apenas leitura, mas alterações nos estados serão ignorados até que ele seja marcado como de escrita.
close() Fecha a sessão e a conexão com a base de dados. Libera outros recursos (como cache). Naão devemos realizar qualquer operações sobre o objeto de sessão após chamar close().
getSessionFactory() Retorna uma referência do objeto SessionFactory que criou a instância do Session atual.
connection() Retorna uma referência da conexão da base de dados.
disconnect() Desconecta da conexão atual com a base de dados.
reconnect() Reconecta com a base de dados.
isConnected() Determina se a conexão com a base de dados está conectada.

Tabela 1: Operações disponíveis nos objetos Session.

Nos casos mais comuns usaremos as Sessões para criar, atualizar, pesquisar e deletar objetos. A lógica para essas operações são inacreditavelmente simples de serem executadas no Hibernate. Abaixo serão demonstrados diversos exemplos de como usar as principais operações das Sessões, começaremos com a operação de inserção.

Listagem 1: Criando objetos e armazenando as informações na base de dados.


try {
begin();
Usuario usuario = new Usuario(username,senha);
getSession().save(usuario);
commit();
return usuario;
} catch( HibernateException e ) {
rollback();
throw new Exception("Não foi possível criar o usuário " + username,e);
}

No código acima iniciamos uma transação, criamos um novo objeto Usuario, solicitamos para o nosso objeto Session para salvar o objeto e então efetuar (commit) a transação. Se um problema é encontrado, como por exemplo um usuário que já foi criado anteriormente na base de dados, então uma exceção do Hibernate será lançada e a transação inteira será desfeita (rolled back).

Para retornar objetos da base de dados, usaremos a linguagem HQL que é similar a linguagem SQL, mas essa linguagem irá referenciar nomes usados no mapeamento das tabelas ao invés de nomes de tabelas e colunas usados nas base de dados. Ela é uma poderosa linguagem de consulta que se parece muito com a SQL, mas a HQL é totalmente orientada a objeto, incluindo os paradigmas de herança, polimorfismo e encapsulamento. No Hibernate, podemos escolher tanto em usar a SQL quanto a HQL. Escolhendo a HQL, poderemos executar as solicitações SQL sobre as classes de persistência do Java ao invés de tabelas no banco de dados.

Um exemplo de uma consulta usando HQL para retornar usuários que possuem um determinado username seria algo como:

from Usuario where username= :username

Onde o Usuario seria o nome da classe que é onde mapeamos a nossa tabela do banco de dados para objetos e “:username” seria um parâmetro que nosso código popula. Esse código seria similar ao SQL abaixo realizado com um Prepared Statement:

select * from usuario where username = ?

Segue abaixo um exemplo mais completo para recuperarmos um usuário especifico através do seu username:

Listagem 2: Recuperando usuários da base de dados.


try {
begin();
Query q = getSession().createQuery("from Usuario where username = :username");
q.setString("username",username);
Usuario usuario = (Usuario)q.uniqueResult();
commit();
return usuario;
} catch( HibernateException e ) {
rollback();
throw new Exception("Não possível retornar o usuário " + username,e);
}

No código acima iniciamos uma transação, criamos um objeto Query (similar ao PreparedStatement), populamos o parâmetro da consulta com o username apropriado, e então listamos os resultados da consulta. Extraímos um usuário (caso algum tenha sido recuperado com sucesso) e por fim confirmamos a transação com um commit. A linha chave usada para obter a entidade Usuario é:

Usuario usuario = (Usuario)q.uniqueResult();

Nós usamos o método uniqueResult() pois este método garante que se forem retornados mais de um objeto Usuario com o mesmo username teremos uma exceção lançada.

Para deletarmos um usuário do banco de dados a lógica é ainda mais simples. Segue abaixo um código de exemplo:

Listagem 3: Deletando um usuário da base de dados.


try {
	begin();
	getSession().delete(usuario);
	commit();
} catch( HibernateException e ) {
	rollback();
	throw new Exception("Não foi possível deletar o usuário " + usuario.getNome(),e);
}

No código acima simplesmente iniciamos uma transação, deletamos um objeto Usuario da base de dados e confirmamos a transação (commit). A transação será desfeita (roll back) se algum problema for encontrado.

Conclusão

Neste artigo, vimos o que é Hibernate e o que são as sessões do Hibernate. Também aprendemos quais são as operações suportadas pelas sessões e como podemos usar as sessões mais utilizadas na prática tais como: inserção, atualização, consulta e deleção. Por fim, fizemos um exemplo para cada uma das operações mais importantes das sessões.