Configuração através do arquivo hibernate.cfg.xml - Projetos

Propaganda
UNIVERSIDADE FEDERAL DE SANTA CATARINA – UFSC
CENTRO TECNOLÓGICO – CTC
DEPARTAMENTO DE INFORMÁTICA E ESTATÍSTICA – INE
CURSO DE SISTEMAS DE INFORMAÇAO
ARTIGO – Framework Hibernate
Daniel Costa Smolenaars
Fabíola Pavan Costa
João Bosco Mangueira Sobral
Orientador
Florianópolis, outubro de 2004.
Introdução
Este tutorial introdutório aborda basicamente os 7 (dos 17) primeiros capítulos do manual de
referência do Hibernate (http://www.hibernate.org) de forma resumida. O objetivo é passar as
informações necessárias para que o leitor possa começar a persistir objetos e tenha base para
entender os conceitos mais avançados do Hibernate. Para isso, começaremos mostrando como o
Hibernate foi projetado e como funciona e depois como utilizá-lo.
Mas afinal, o que é o Hibernate? De acordo com seus criadores, Hibernate é um serviço de
consulta e persistência Objeto/Relacional para Java, muito poderoso e de alto desempenho. A
grande maioria dos desenvolvedores de sistemas ainda opta por utilizar bancos de dados
relacionais pela confiabilidade e robustez, embora o paradigma utilizado seja o Orientado a
Objetos. Dessa forma, os registros do banco de dados devem ser transformados em objetos e as
informações contidas nos objetos devem ser persistidas em forma de linhas e colunas. Chamamos
isso de “Mapeamento Objeto-Relacional”.
Muitas vezes, os desenvolvedores acabam consumindo muito tempo de desenvolvimento para
fazer este mapeamento. A proposta do Hibernate é exatamente prover aos desenvolvedores uma
maneira de realizar este mapeamento de forma transparente, isto é, criando classes como se não
houvesse persistência relacional. Mas o Hibernate não provê apenas simples mapeamento de uma
classe para uma tabela. Relacionamentos, Herança, Polimorfismo, Composições e Coleções são
algumas dos conceitos OO contemplados pelo Hibernate.
Ele também possui o HQL (Hibernate Query Language), uma linguagem de consultas orientada a
objetos muito interessante! Hibernate suporta uma enorme quantidade de bancos de dados,
incluindo DB2, PostgreSQL, MySQL, Oracle, Sybase, SQL Server, dentre outros. E além de tudo
isso, Hibernate é um software livre. Qualquer pessoa pode utilizá-lo em aplicações domésticas e
comerciais de acordo com a LGPL (http://www.gnu.org/copyleft/lesser.html).
Arquitetura
Dependendo da complexidade do projeto, um maior número de APIs e componentes são
utilizados pelo
Hibernate. A figura abaixo exibe aqueles que são necessários em um sistema mais simples
possível:
De acordo com a especificação do Hibernate, estes componentes são definidos da seguinte forma:





SessionFactory (net.sf.hibernate.SessionFactory) Armazena os mapeamentos e
configurações compilados. É uma fábrica de objetos Session. Também provê conexões.
Este objeto é imutável e threadsafe (pode ser acessado por múltiplas threads sem perigo
de inconsistência).
Session (net.sf.hibernate.Session) Um objeto que representa o "diálogo" entre a aplicação
e apersistência (banco de dados, etc), encapsulando uma conexão JDBC. Este objeto não
deve ser manipulado por múltiplas threads. Ele controla um cache dos objetos
persistentes. Os Session não devem durar toda a execução da aplicação, ou seja, são
objetos chamados "de vida curta".
Persistent Objects (objetos persistentes) Objetos manipulados por somente uma thread
que contém informações persistentes e lógica de negócio. Devem ser JavaBeans (possuir
um construtor sem parâmetros e métodos get/set para os atributos persistidos). Eles só
podem estar associados à exatamente uma Session.
Transient Objects (objetos transientes) Instâncias das classes persistentes que não estão
atualmente associadas a uma Session. Podem ter sido instanciados pela aplicação mas
não persistidos ainda, ou recuperados por uma Session que foi fechada.
Transaction (net.sf.hibernate.Transaction) Objeto usado pela aplicação para especificar
unidades atômicas de acesso ao banco. Não deve ser manipulado por múltiplas threads.
Abstrai a aplicação de transações JDBC, JTA ou CORBA. Uma Session pode manipular
várias Transaction.
Além de seus próprios componentes, o Hibernate utiliza APIs de outros projetos (como drivers
JDBC do banco de dados). Por isso é preciso prover todos os JARs (bibliotecas) que o Hibernate
vai precisar. Felizmente, quase todos vêem na distribuição padrão do Hibernate. A próxima seção
explica como instalar o Hibernate.
Instalação
Instalar o Hibernate é simples. Vá ao site oficial e baixe o arquivo de instalação da última versão
estável. Ao descompactá-lo, um diretório é criado. Este diretório contém o JAR núcleo do
Hibernate (hibernate2.jar, para a versão 0.2.x). Também há um subdiretório chamado lib. Ali
estão os JARs das outras APIs utilizadas por ele.
Certifique-se de que esses JARs estejam no CLASSPATH de sua aplicação e você poderá
importar as classes do Hibernate. É necessário também que a classe de driver do seu banco de
dados também esteja no CLASSPATH. Como visto na seção Arquitetura, quais dessas APIs serão
efetivamente utilizadas pelo Hibernate depende da complexidade do seu projeto.
Configurando o SessionFactory
Hibernate foi projetado para operar em diferentes ambientes. Por isso, existe um grande número
de parâmetros de configuração. A maioria deles já possui valores padrão e, além disso, Hibernate
vem com o arquivo hibernate.properties que mostra as várias opções.
A lista abaixo contém os parâmetros mais importantes para configuração da conexão JDBC (com
banco de dados).
hibernate.connection.driver_class Classe do driver JDBC
hibernate.connection.url URL JDBC
hibernate.connection.username Usuário do banco de dados
hibernate.connection.password Senha do usuário do banco de dados
hibernate.connection.pool_size Número de conexões disponíveis
O Hibernate possui ainda uma série de parâmetros que definem seu comportamento em tempo de
excução. Nesta introdução vamos abordar apenas os dois da lista abaixo:
hibernate.dialect Subclasse de net.sf.hibernate.dialect.Dialect que contém características
particulares de cada banco de dados
hibernate.show_sq Exibir as declarações SQL executadas no console
O Hibernate possui dialetos para um grande número de bancos de dados tais como: DB2,
MySQL, SAP DB, Oracle, Oracle 9, Sybase, Sybase Anywhere, Progress, Mckoi SQL, Interbase,
Pointbase, PostgreSQL, HypersonicSQL, Microsoft SQL Server, Ingres, Informix e FrontBase.
Vamos usar como exemplo o banco de dados MySQL, cuja classe é
net.sf.hibernate.dialect.MySQLDialect. As classes dos outros bancos de dados estão descritas no
manual de referência do Hibernate.
Estes parâmetros são usados para configuração inicial. A partir deles, é criado um objeto da
classe SessionFactory. Este objeto contém todas as informações passadas na configuração. Após
sua criação, as configurações podem ser descartadas. Quando criado, o SessionFactory carrega (e
avalia) todas as configurações. Esta operação é a que despende mais tempo no Hibernate. Por
isso, é recomendável criar este objeto somente uma vez e utilizá-lo durante toda execução da
aplicação. O Hibernate permite que sejam criados mais de um SessionFactory, mas isso só deve
ser feito se houver necessidade de acesso a mais de um banco de dados. Existem duas formas de
informar ao SessionFactory as configurações do Hibernate.
Configuração através do objeto Configuration
A configuração pode ser feita através da classe Configuration
(net.sf.hibernate.cfg.Configuration). Os objetos desta classe podem ser instanciados de forma
direta. Configuration deve receber as configurações gerais (através da classe Properties) e
também os mapeamentos Objeto/Relacional (apresentados posteriormente). Veja abaixo um
exemplo de como criar um SessionFactory através do objeto Configuration:
Configuration cfg = new Configuration()
.setProperty("hibernate.connection.driver_class", "com.mysql.jdbc.Driver")
.setProperty("hibernate.connection.url", "jdbc:mysql://127.0.0.1/NomeDoBanco")
.setProperty("hibernate.connection.username","usuario")
.setProperty("hibernate.connection.password","senha")
.setProperty("show_sql","true")
.setProperty("dialect","net.sf.hibernate.dialect.MySQLDialect")
.addFile("MapeamentoObjetoRelacional.hbm.xml"); //Apresentado posteriormente
try{
SessionFactory sf = cfg.buildSessionFactory();
}catch(HibernateException he){
he.printStackTrace();
}
Configuração através do arquivo hibernate.cfg.xml
Uma outra forma (talvez a mais utilizada) de criar o SessionFactory é através de um arquivo de
configuração XML chamado hibernate.cfg.xml, como no exemplo abaixo:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">
com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">
jdbc:mysql://127.0.0.1/NomeDoBanco</property>
<property name="hibernate.connection.username">usuario</property>
<property name="hibernate.connection.password">senha</property>
<property name="show_sql">true</property>
<property name="dialect">
net.sf.hibernate.dialect.MySQLDialect</property>
<!--Apresentado Posteriormente-->
<mapping resource="MapeamentoObjetoRelacional.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Este arquivo de configuração deve ser, então, processado para criar um objeto Configuration. O
objeto resultante é utilizado para criar o SessionFactory. Se o arquivo estiver no CLASSPATH
não é necessário passar nenhum parâmetro para o método configure (neste caso o nome do
arquivo tem que ser hibernate.cfg.xml), caso contrário o caminho deve ser especificado (e seu
nome é livre):
try{
Configuration cfg = new
Configuration().configure("/engenho/hibernate.cfg.xml");
SessionFactory sf = cfg.buildSessionFactory();
}catch(HibernateException he){
he.printStackTrace();
}
Mapeamento O/R Básico
O Hibernate usa arquivos XML para mapear os atributos das classes em campos das tabelas.
Qualquer classe pode ser mapeada desde que seja um Bean, ou seja, possua um construtor sem
parâmetros e os atributos mapeados possuam métodos get e set. A presença de um atributo de
identificação (chave primária) é altamente recomendada, mas não é obrigatória, caso contrário
diversas funcionalidades não estarão disponíveis. Não é necessário nenhum tipo de especialização
(herança) ou realização de interface para que uma classe seja persistida pelo Hibernate. Isto quer
dizer que o Hibernate pode persistir objetos POJO (Plain Old Java Object). Veja abaixo o
exemplo de uma classe que poderia ser persistida:
Abaixo, a implementação da classe do diagrama UML acima.
package engenho;
import java.util.Date;
public class Pessoa {
private Long idPessoa;
private String nome;
private Date dataNascimento;
public Pessoa() {
}
public Long getIdPessoa() { return idPessoa; }
public void setIdPessoa(Long idPessoa) { this.idPessoa = idPessoa; }
public String getNome() {return nome;}
public void setNome(String nome) { this.nome = nome; }
public Date getDataNascimento() { return dataNascimento; }
public void setDataNascimento(Date dataNascimento){
this.dataNascimento = dataNascimento;
}
}
Veja a declaração de criação da tabela que armazena a classe acima. Aconselha-se escolher
identificadores de tipos de grande capacidade (como BIGINT) para que um grande número de
registros possa ser armazenado. Note o atributo AUTO_INCREMENT para a chave primária
idPessoa, logo em seguida fica claro o porquê disso.
CREATE TABLE Pessoa (
idPessoa BIGINT NOT NULL AUTO_INCREMENT,
nome VARCHAR(50) NOT NULL,
dataNascimento DATETIME NULL,
PRIMARY KEY(idPessoa)
) TYPE=InnoDB;
Para mapear uma classe numa tabela:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class name="engenho.Pessoa" table="Pessoa">
<id name="idPessoa">
<generator class="identity" />
</id>
<property name="nome"/>
<property name="dataNascimento"/>
</class>
</hibernate-mapping>
Os arquivos de mapeamento são fáceis de configurar e divididos de maneira bastante intuitiva.
Além disso, a maioria dos parâmetros existentes possui valores padrão. Este exemplo inicial
exibe apenas os parâmetros obrigatórios (com exceção do elemento <id>).
É obrigatório especificar o nome da classe e a tabela com a qual está associada. O elemento <id>
indica qual é o identificador do objeto e como ele é criado e obtido. Caso ele seja omitido, o
Hibernate considera que a classe não possui identificador. O atributo name indica que atributo da
classe será usado como identificador. O elemento generator informa como serão gerados os
identificadores dos novos elementos. Veja alguns dos tipos de geradores existentes no Hibernate:


increment - Gera identificadores dos tipos long, short ou int que são únicos apenas
quando nenhum outro processo está inserindo na mesma tabela. Não deve ser usado em
clusters identity usado para colunas identity nos bancos DB2, MySQL, MS SQL Server,
Sybase e HypersonicSQL. O identificador retornado é do tipo long, short ou int
sequence Usado para sequence nos bancos DB2, PostgreSQL, Oracle, SAP DB, McKoi
ou em um generator no banco Interbase. O identificador retornado é do tipo long, short
ou int hilo Utiliza o algorítmo "High / Low" para geração eficiente de identificadores de
objeto do tipo long, short ou int, dada uma tabela e uma coluna desta tabela (por default,


hibernate_unique_key e next respectivamente) como fonte de valores "high". O algorítmo
gera identificadores que são únicos somente para um banco de dados em particular.
assigned - Deixa que a aplicação atribua o identificador antes que os métodos de
persistência (save() ou saveOrUpdate()) sejam chamados
foreign - Usa o identificador de um objeto associado. Comumente usado em conjunto
com uma associação um para um.
Cada atributo da classe é mapeado através do elemento <property>. O único parâmetro
obrigatório é o nome do atributo. O Hibernate tentará encontrar uma coluna com este mesmo
nome e definir seu tipo por reflexão.
Abaixo, o mapeamento desta mesma classe com os parâmetros opcionais (use o mapeamento
simples para executar como exemplo, note que os nomes das colunas foram trocados).
<hibernate-mapping>
<class name="engenho.Pessoa" table="Pessoa" dynamic-update="true">
<id name="idPessoa" column="ID_PESSOA" type="long" unsaved-value="0">
<generator class="identity" />
</id>
<property name="nome"
column="NOME"
type="string"
not-null="true"/>
<property name="dataNascimento"
column="DATA_NASCIMENTO"
type="java.util.Date"
not-null="false"/>
</class>
</hibernate-mapping>
O primeiro parâmetro a notar é column. Ele indica a coluna correspondente ao atributo da classe
na tabela, caso não tenham o mesmo nome. O parâmetro type, indica o tipo do atributo Hibernate.
Veja a associação entre os tipos do Hibernate mais comuns, as classes wrapper Java e os tipos no
banco de dados:
Tipo Hibernate Classe Java Tipo no Banco de Dados
integer, long, short, float, double, character, byte, boolean e os tipos yes_no, true_false (ambos
compatíveis com boolean Java.lang.Integer, java.lang.Long, java.lang.Short, java.lang.Float,
java.lang.Double, java.lang.Character, java.lang.Byte, java.lang.Boolean e os tipos .
Cada banco possui tipos específicos para estes tipos Java e Java.lang.Boolean) java.lang.Boolean
e os tipos primitivos int, long, short, float, double, character, byte e Boolean string
java.lang.String VARCHAR (VARCHAR2 para Oracle) date, time, timestamp java.util.Date
DATE, TIME e TIMESTAMP (ou equivalentes) calendar, calendar_date java.util.Calendar
TIMESTAMP e DATE (ou equivalentes) big_decimal java.math.BigDecimal NUMERIC
(NUMBER para Oracle) locale, timezone, currency java.util.Locale, java.util.TimeZone e
java.util.Currency VARCHAR (VARCHAR2 para Oracle) class java.lang.Class VARCHAR
(VARCHAR2 para Oracle) binary Array de bytes Tipo binário específico de cada banco
text java.lang.String CLOB ou TEXT serializable Qualquer classe serializável Tipo binário
específico de cada banco clob, blob java.sql.Clob e java.sql.Blob. CLOB e BLOB
No mapeamento, pode ser informado na propriedade type tanto o tipo Hibernate quando a classe
Java. Outra propriedade opcional importante é not-null. Ela indica que no momento da
persistência de um objeto, o determinado atributo pode ser nulo ou não. Caso um atributo esteja
indicado como not-null=”true” e no momento do salvamento ele esteja null, Hibernate irá lançar
uma exceção.
Lembre-se de incluir a linha abaixo no arquivo do hibernate.cfg.xml. Ela indica que este
mapeamento deve estar contemplado no SessionFactory.
<mapping resource="engenho/Pessoa.hbm.xml"/>
Manipulando objetos persistentes
Para persistir e recuperar os objetos, o Hibernate provê objetos chamados Session. Session pode
ser considerado uma sessão de comunicação com o banco de dados através de uma conexão
JDBC. Hibernate lhe permite, mas não é recomendável, manipular suas próprias conexões.
O exemplo abaixo demonstra a criação e persistência de um objeto do tipo Pessoa.
try{
Session s = sf.openSession();
Pessoa novaPessoa = new Pessoa();
novaPessoa.setNome("Bruno");
Calendar c = Calendar.getInstance();
c.set(1981, 4, 6);
novaPessoa.setDataNascimento(c.getTime());
s.saveOrUpdate(novaPessoa);
s.connection().commit();
s.close();
}catch(HibernateException he){
he.printStackTrace();
}catch(SQLException sqle){
sqle.printStackTrace();
}
O método saveOrUpdate(), apresentado no código acima, pode ser usado tanto para inserções
quanto para atualizações. A chamada a esse método indica à Session que o estado do objeto
passado como parâmetro deve ser persistido. Se o método commit() da connection associada à
Session não for chamado, as alterações não serão efetivadas no banco de dados. Existe ainda um
método chamado flush(). Este é útil para enviar imediatamente ao banco de dados todas as
declarações de manipulação da Session (por exemplo, um INSERT caso um objeto tenha sido
inserido). Este método é chamado automaticamente a cada commmit().
O Hibernate disponibiliza ainda um objeto Transaction (net.sf.hibernate.Transaction) para
controle das modificações. Veja as possibilidades no trecho de código abaixo:
Session s = null;
Transaction t = null;
try{
s = sf.openSession();
Pessoa pessoa1 = (Pessoa) s.load(Pessoa.class, new Long(1));
Pessoa pessoa2 = (Pessoa) s.load(Pessoa.class, new Long(2));
t = s.beginTransaction();
pessoa1.setNome("Marta");
s.flush();
pessoa2.setNome("Penha");
s.flush();
t.commit();
}catch(HibernateException he){
t.rollback();
}finally{
s.close();
}
Enquanto SessionFactory é pesado e lento para criar, os objetos Session são leves e rápidos.
O exemplo anterior já apresenta o método load(), que permite recuperar um único objeto do
banco através de seu identificador. Entretanto, muitas vezes queremos obter uma lista dos objetos
existentes. Isto pode ser feito através do método find().
Session s = sf.openSession();
List pessoas = s.find("from Pessoa");
for(int indice=0; indice<pessoas.size(); indice++){
Pessoa pessoa = (Pessoa)pessoas.get(indice);
System.out.println(pessoa.getNome());
}
s.close();
O método find(), apresentado acima, recebe como parâmetro uma String que deve ser a consulta
HQL (Hibernate Query Language). Essa linguagem é muito semelhante ao SQL (Structured
Query Language), mas é 100% orientada a objetos. A HQL será abordada nos futuros tutoriais.
Conclusão
O Hibernate consegue combinar duas características que se almeja em praticamente todos os
softwares: Simplicidade e Robustez. Este tutorial apresentou conceitos que permitem ao leitor
iniciar o uso e aprendizado do Hibernate de forma clara e direta.
Para quem deseja saber mais, consulte o manual de referência do Hibernate e os fóruns de
discussão na Internet.
Download