Você está na página 1de 51

Por

Ana Cláudia Cabral


Jul/2008

NHibernate – Persistência de Dados em Plataforma .NET 0


Súmario
1.Conceitos Gerais de Persistência .........................................................................03

2. Mapeamento Objeto Relacional ..........................................................................03


2.1 Mapeamento objeto relacional ...........................................................................04
2.1.1 Mapeamento de um objeto com tipos primitivos ................................04
2.1.2 Mapeamento de objetos que contém uma coleção de objetos .............05

3. Introdução ao Nhibernate .....................................................................................06


3.1 NHibernate x Hibernate .....................................................................................07

4. Arquitetura .................................................................................................................07
4.1 Definições das principais interfaces dos diagramas ...........................................09

5. Objetos Persistentes, Transientes e Detached ................................................10


5.1 Ciclo de Vida – Persistência ...............................................................................11
5.2 Sessões contextuais ............................................................................................12

6. Classes Persistentes .................................................................................................12


6.1 Considerações ....................................................................................................13
6.2 Identidade/Igualdade de Obetos (Equals () e GetHashCode () ) ........................13
6.3 Escolhendo Chaves Primárias ............................................................................15

7. Configurando o NHibernate ...............................................................................15

8. Manipulando Objetos Persistentes.....................................................................17


8.1 Criando um Objeto persistente ...........................................................................17
8.2 Carregando um objeto ........................................................................................17
8.3 Busca ..................................................................................................................18
8.4 Atualizando objetos ............................................................................................20
8.5 Deletando um objeto ..........................................................................................20
8.6 Flush ...................................................................................................................21
8.7 Encerrar uma sessão ...........................................................................................21

9. Mapeando O/R básico ............................................................................................22


9.1. Declaração de Mapeamento .............................................................................22
9.1.1. XML Namespace.................................................................................24
9.1.2. Hibernate-mapping.............................................................................24
9.1.3. Classes……........................................................................................25
9.1.4. Id ........................................................................................................25
9.1.4.1. Gerador.................................................................................26
9.1.5. Composite-ID.....................................................................................28
9.1.6. Discriminador ....................................................................................29

NHibernate – Persistência de Dados em Plataforma .NET 1


9.1.7. Propriedade.........................................................................................30
9.1.8. many-to-one.........................................................................................32
9.1.9. one-to-one............................................................................................33
9.1.10. subclass..............................................................................................34
9.2. NHibernate Types...............................................................................................34
9.2.1. Tipos Básicos ......................................................................................34
10. Mapeando coleções.................................................................................................37

11. Transações.................................................................................................................38
11.1 Modelos de Transações ....................................................................................40
11.2 Transações e Banco de Dados...........................................................................40

12. Concorrência............................................................................................................41

13. HQL – a linguagem de consulta hibernate....................................................42


13.1 Case-Sensitive...................................................................................................42
13.2 A cláusura FROM.............................................................................................42
13.3 Associações e junções ......................................................................................43
13.4 A cláusura select...............................................................................................43
13.5 Funções Agregadas...........................................................................................43
13.6 Consultas Polimórficas......................................................................................44
13.7 Cláusura WHERE.............................................................................................44
13.8 Expressões........................................................................................................45
13.9 A cláusura Group By........................................................................................46
13.10 A cláusura Order By.......................................................................................46
13.11 Subqueries......................................................................................................46

14. Criteria......................................................................................................................47
14.1 Criando uma instância ICriteria ......................................................................47
14.1.1 Criteria com restrição........................................................................47
14.1.2 Criteria com restrições Like .............................................................47
14.2 Ordenando o resultado.....................................................................................48
14.3 Associações .....................................................................................................48

15. Native SQL Queries.............................................................................................48


15.1 Criando uma consulta(IQuery) baseada em SQL ............................................48
15.2 Referências de alias e propriedade...................................................................48

16. Consultas nomeadas SQL....................................................................................49

Referências ......................................................................................................................50

NHibernate – Persistência de Dados em Plataforma .NET 2


1.Conceitos Gerais de Persistência
Imagine um usuário fazendo uso de uma aplicação, por exemplo, um
sistema para controlar suas finanças. Ele passa a fornecer como dados de entrada
todos os seus gastos mensais para que a aplicação lhe gere, por exemplo, gráficos
nos quais ele possa avaliar seus gastos. Finalizada, a sua análise financeira, o
usuário resolve desligar o computador em que a aplicação se encontra. Imagine
agora que o usuário teve novos gastos, voltou a ligar o computador, acessou
novamente o sistema de finanças e que gostaria de realizar novas análises com
todos os seus gastos acumulados. Se a aplicação não armazenar, de alguma
forma, os primeiros gastos fornecidos, o usuário teria que informá-los novamente
e em seguida, acrescentar os novos gastos para fazer a nova análise, causando
grande trabalho e o sistema não sendo eficiente.
Para resolver esse tipo de problema, uma solução seria armazenar (persistir)
os dados lançados a cada vez pelo usuário em um banco de dados relacional,
utilizando SQL (Structured Query Language).

2. Mapeamento Objeto Relacional


Por vários anos os projetos de aplicações corporativas tiveram uma forte
necessidade de se otimizar a comunicação da lógica de negócio com a base de
dados. Essa necessidade ganhou mais intensidade com o crescimento dessas
aplicações, crescimento esse, tanto em requisitos (funcionalidade) quanto em
volume de dados armazenados em seu banco de dados.

Na década de 80, foram criados os bancos de dados relacionais (BDR) que


substituíram as bases de dados de arquivos. Para esse tipo de base de dados foi
criada uma linguagem, a SQL. Essa linguagem foi toda baseada na lógica relacional
e por isso contava com diversas otimizações em suas tarefas se comparadas com
as outras tecnologias existentes. A partir de então foi diminuído o tempo gasto para
as operações de persistência, mesmo com um grande volume de dados. Entretanto,
essa linguagem não propiciava aos desenvolvedores uma facilidade para que a
produtividade fosse aumentada.

Uma solução que surgiu no início da década de 90 foi à criação de um


modelo de banco de dados baseado no conceito de orientação a objetos. Este
modelo visava facilitar, para os desenvolvedores, a implementação da camada de
persistência da aplicação, pois eles já estavam familiarizados com o paradigma de
orientação a objetos, consequentemente, a produtividade certamente aumentaria.

NHibernate – Persistência de Dados em Plataforma .NET 3


Na prática, esse modelo de dados não foi utilizado em grandes aplicações, visto que
elas tinham um volume de dados muito grande e esse modelo era ineficiente em
termos de tempo de resposta, pois ao contrário dos bancos de dados relacionais,
eles não tinham um modelo matemático que facilitasse as suas operações de
persistências.

Então a solução foi usar os BDR e desenvolver ferramentas para que o seu
uso seja facilitado. Uma dessas ferramentas é o framework NHibernate que usa o
conceito de mapeamento objeto relacional (MOR).
As subseções seguintes apresentam os conceitos relacionados ao
mapeamento objeto relacional.

2.1 Mapeamento objeto relacional

Como foi descrito anteriormente, mapeamento objeto relacional funciona


com a transformação dos dados de um objeto em uma linha de uma tabela de um
banco de dados, ou de forma inversa, com a transformação de uma linha da tabela
em um objeto da aplicação. Abordando essa idéia, alguns problemas poderão
existir, como, se um objeto tiver uma coleção de outros objetos. Nas próximas
subseções serão abordados os alguns tipos de mapeamentos básicos.

2.1.1 Mapeamento de um objeto com tipos primitivos

Esse é o mapeamento mais simples, onde um objeto tem apenas tipos de


dados básicos. Vale salientar que entendesse por tipos básicos aqueles que
possuem um correspondente em SQL, ou seja, o tipo String da linguagem Java é
considerado um tipo básico, pois ele possui um correspondente em SQL.
Na Figura 1, observa-se o mapeamento de três objetos do tipo Veiculo na
tabela de um banco de dados. Caso a aplicação deseje saber o veículo da cor
vermelha, por exemplo, então o objeto que tem o Pálio como modelo é retornado.

NHibernate – Persistência de Dados em Plataforma .NET 4


2.1.2 Mapeamento de objetos que contém uma coleção de objetos

Esse tipo de mapeamento é quando um objeto possui um conjunto de outros


objetos. Para obter esse conceito é necessário adicionar, ao exemplo da Figura 1,
uma nova classe chamada de Fabricante que conterá as informações: nome, que
armazenará o nome desse fabricante e o veículo, que conterá o conjunto de
veículos do fabricante (telefone e endereco não são informações relevantes no
exemplo). Faz-se necessário a adição de uma informação na classe Veiculo
chamada de fabricante, como mostrado na Figura 2.

O atributo fabricante adicionado em Veiculo é simplesmente para


relacionar um veículo ao seu fabricante, enquanto o veiculos de Fabricante
referencia a classe Veiculo.
Como foram realizadas mudanças no domínio do exemplo orientado a
objetos, faz-se necessária uma alteração no modelo do banco de dados.
Primeiramente, o vínculo que foi realizado entre um fabricante e um veículo deverá
ser implementado através de uma chave estrangeira (referência a uma outra
tabela, pois em BDR não existe o conceito de coleções) que se localizará na tabela
VEICULO, caracterizando o mapeamento 1 para N, ou seja, um veículo possui
apenas um fabricante e um fabricante possui vários (N) veículos. Essa chave
estrangeira será realizada entre a informação nome da classe Fabricante e o
atributo fabricante da classe Veiculo.

NHibernate – Persistência de Dados em Plataforma .NET 5


Para um melhor entendimento, a Figura 3 mostra o mapeamento dos
objetos para as tabelas no BDR. Nele observa-se que cada veículo está associado
com um fabricante, isso implica dizer que na tabela de VEICULO existe uma
referência para a tabela FABRICANTE. Essa associação é feita através da coluna
fabricante da tabela VEICULO. Pensando em termos práticos, quando um veículo
for inserido no banco de dados, ele será ligado ao seu respectivo fabricante, no
momento da inserção.

Para recuperar os veículos inseridos o desenvolvedor terá que implementar


uma busca que retorne além das informações do veículo as informações do seu
fabricante, isso poderá ser realizado fazendo a seguinte consulta SQL: SELECT *
FROM fabricante WHERE nome = <fabricante>, onde <fabricante> é o nome
do fabricante que está na tabela VEICULO, por exemplo, caso a aplicação deseje
as informações do veículo do modelo Pálio, então para se buscar o fabricante a
seguinte consulta será realizada: SELECT * FROM fabricante WHERE nome =
'Fiat'.

Utilizando a mesma idéia, os fabricantes são buscados; a única diferença é


que agora a consulta que buscará os veículos associados com o fabricante retornará
uma coleção de veículos (SELECT * FROM veiculo WHERE fabricante =
<nomeFabricante>).

Esse tipo de mapeamento é muito utilizado em aplicações em que os objetos


fazem muita referência a outros objetos.
Com a idéia do MOR vários projetos de ferramenta começaram a ser
desenvolvidas, visando facilitar a implementação da camada de persistência, dentre
elas o framework NHibernate, que um software livre de código aberto e que está
tendo uma forte adesão de novos projetos corporativos. Essa ferramenta será
descrita nas próximas seções.

3. Introdução ao Nhibernate
Trabalhar com software orientado a objeto e uma banco de dados relacional pode
ser incômodo e demorado em ambientes empresariais de hoje. O NHibernate é um
objeto/ferramenta de mapeamento relacional para ambientes .NET. O termo objeto de
mapeamento relacional (ORM) refere à técnica de mapear uma representação de dados
de um modelo de objeto a um modelo de dados relacional com um schema baseado em
SQL.

O NHibernate mapeia não somente as classes .NET das tabelas da banco de dados (e
dos tipos de dados .NET aos tipos de dados do SQL), mas também fornece facilidades
da pergunta e da recuperação dos dados e pode significativamente reduzir o tempo de

NHibernate – Persistência de Dados em Plataforma .NET 6


desenvolvimento fazendo de outra maneira a manipulação de dados manual no SQL e
no ADO.NET.

O objetivo de NHibernate é aliviar o desenvolvedor em 95 % das tarefas de


programação relativas aos dados de persistência comum. O NHibernate pode não ser a
melhor solução para as aplicações data-centric que usam somente stored-procedures
para executar a lógica de negócio na banco de dados, ele é mais útil com modelos de
domínio orientados a objeto e lógica de negócio middle-tier baseado em .NET.
Entretanto, o NHibernate pode certamente ajudá-lo a remover ou encapsular o código
SQL vendor-specific e o ajudará com tarefas comuns de tradução ajustada ao resultado
de uma representação tabular para um gráfico de objetos.

3.1 NHibernate x Hibernate


Atualmente, o NHibernate encontra-se na versão 1.0.2 e pode ser utilizado tanto nos
Frameworks 1.1 e 2.0 da plataforma .NET. Esta versão do NHibernate é equivalente à
versão 2.1 do J2EE, e toda a documentação utilizada na versão J2EE pode ser aplicada
diretamente para a versão do .NET.
Uma curiosidade é que após o lançamento oficial da versão 1.0 do NHibernate, a
JBOSS, empresa responsável pelo Hibernate, comprou os direitos desta ferramenta,
fazendo assim uma integração do conhecimento, usuários e plataforma para um modelo
de persistência de dados padrão entre as tecnologias.
Se você acessar os sites www.hibernate.org e www.nhibernate.org notará que ambos
compartilham da mesma estrutura. Algumas particularidades das versões podem ser
encontradas no link: http://www.hibernate.org/360.html. Entretanto, a JBOSS vem
trabalhando para tentar zerar todas as pendências.

4. Arquitetura
A arquitetura do NHibernate é formada basicamente por um conjunto de
interfaces. Os diagramas abaixo apresentam as interfaces mais importantes nas camadas de
negócio e persistência. A camada de negócio aparece acima da camada de
persistência por atuar como uma cliente da camada de persistência. Vale salientar
que algumas aplicações podem não ter a separação clara entre as camadas de
negócio e de persistência.
De acordo com os diagramas abaixo, as interfaces são classificadas como:

• Interfaces responsáveis por executar operações de criação, deleção,


consulta e atualização no banco de dados: ISession, ITransaction e
Query;
• Interface utilizada pela aplicação para configurar o NHibernate:
IConfiguration;
Interfaces responsáveis por realizar a interação entre os eventos do
NHibernate e a aplicação: Interceptor, Lifecycle e Validatable.

NHibernate – Persistência de Dados em Plataforma .NET 7


• Interfaces que permitem a extensão das funcionalidades de
mapeamento do NHibernate: UserType, CompositeUserType,
IdentifierGenerator.

Uma (muitas) visão de alto nível da arquitetura do NHibernate:

Este diagrama mostra o NHibernate usando os dados do banco de dados e a configuração


para fornecer serviços de persistência (e objetos persistentes) para o aplicativo.

Gostaríamos de mostrar um modo de exibição mais detalhado da arquitetura do tempo de


execução. Infelizmente, o NHibernate é flexível e oferece suporte a várias abordagens.
Mostraremos os dois extremos. A arquitetura "Lite" fornece aos seus aplicativos suas
próprias conexões do ADO.NET e gerencia suas próprias transações. Essa abordagem usa
um subconjunto mínimo de APIs do NHibernate:

NHibernate – Persistência de Dados em Plataforma .NET 8


A arquitetura "full cream" abstrai o aplicativo para fora dos APIs ADO.NET subjacente e
permite que o NHibernate cuide dos detalhes.

4.1 Definições das principais interfaces dos diagramas:

• ISessionFactory ( NHibernate.ISessionFactory ) ISessionFactory


(NHibernate.ISessionFactory)

O objeto ISessionFactory é aquele que mantém o mapeamento objeto


relacional em memória. Permite a criação de objetos ISession, a partir dos quais
os dados são acessados. Também denominado como fábrica de objetos ISessions.
Um objeto SessionFactory é threadsafe, porém deve existir apenas uma

NHibernate – Persistência de Dados em Plataforma .NET 9


instância dele na aplicação, pois é um objeto muito pesado para ser criado várias vezes.

• ISession ( NHibernate.ISession ) ISession (NHibernate.ISession)

O objeto ISession é aquele que possibilita a comunicação entre a aplicação e


a persistência, através de uma conexão ADO.NET. É um objeto leve de ser criado, não
deve ter tempo de vida por toda a aplicação e não é threadsafe. Um objeto ISession
possui um cache local de objetos recuperados na sessão. Com ele é possível criar,
remover, atualizar e recuperar objetos persistentes.

• ITransaction ( NHibernate.ITransaction ) ITransaction


(NHibernate.ITransaction)
A interface iTransaction é utilizada para representar uma unidade indivisível
de uma operação de manipulação de dados. O uso dessa interface em aplicações
que usam NHibernate é opcional. Essa interface abstrai a aplicação dos detalhes das
transações ADO.NET.

• Interfaces Criteria e Query


As interfaces Criteria e Query são utilizadas para realizar consultas ao
banco de dados.

5. Objetos Persistentes, Transientes e Detached


Nas diversas aplicações existentes, sempre que for necessário propagar o
estado de um objeto que está em memória para o banco de dados ou vice-versa,
há a necessidade de que a aplicação interaja com uma camada de persistência. Isto
é feito, invocando o gerenciador de persistência e as interfaces de consultas do
NHibernate. Quando interagindo com o mecanismo de persistência, é necessário para
a aplicação ter conhecimento sobre os estados do ciclo de vida da persistência.

Em aplicações orientadas a objetos, a persistência permite que um objeto


continue a existir mesmo após a destruição do processo que o criou. Na verdade,
o que continua a existir é seu estado, já que pode ser armazenado em disco e
então, no futuro, ser recriado em um novo objeto.

Em uma aplicação não há somente objetos persistentes, pode haver


também objetos transientes. Objetos transientes são aqueles que possuem um
ciclo de vida limitado ao tempo de vida do processo que o instanciou. Em relação
às classes persistentes, nem todas as suas instâncias possuem necessariamente
um estado persistente. Elas também podem ter um estado transiente ou
detached.
O NHibernate define estes três tipos de estados: persistentes, transientes e
detached. Objetos com esses estados são definidos como a seguir:

NHibernate – Persistência de Dados em Plataforma .NET 10


• Objetos Transientes: são objetos que suas instâncias não estão nem
estiveram associados a algum contexto persistente. Eles são
instanciados, utilizados e após a sua destruição não podem ser
reconstruídos automaticamente;
• Objetos Persistentes: são objetos que suas instâncias estão
associadas a um contexto persistente, ou seja, tem uma identidade
de banco de dados. Para um contexto específico de persistência, o NHibernate garante que a
identidade persistente é equivalente a identidade do CLR (na memória local do objeto).
• Objetos detached: são objetos que tiveram suas instâncias
associadas a um contexto persistente, mas que por algum motivo
deixaram de ser associadas, por exemplo, por fechamento de
sessão, finalização de sessão. São objetos em um estado intermediário, nem são transientes
nem persistentes. Para instâncias separadas, o NHibernate não dá nenhuma garantia sobre a
relação entre a identidade persistente e identidade do CLR

O ciclo de vida de um objeto persistente pode ser resumido a partir da Figura


abaixo.

5.1 Ciclo de Vida - Persistência

De acordo com a figura acima, inicialmente, o objeto pode ser criado e ter o
estado transiente ou persistente. Um objeto em estado transiente se torna
persistente se for criado ou atualizado no banco de dados. Já um objeto em estado
persistente, pode retornar ao estado transiente se for apagado do banco de dados.
Também pode passar ao estado detached, se, por exemplo, a sessão com o banco
de dados por fechada. Um objeto no estado detached pode voltar ao estado
persistente se, por exemplo, for atualizado no banco de dados. Tanto do estado
detached quanto do estado transiente o objeto pode ser coletado para destruição.

NHibernate – Persistência de Dados em Plataforma .NET 11


5.2 Sessões contextuais

A maioria dos aplicativos que usam o NHibernate precisam de alguma forma de sessões
"contextuais", onde uma determinada sessão está em vigor em todo o escopo de um
determinado contexto. No entanto, todas as definições dos aplicativos que constituem um
contexto são geralmente diferentes; e contextos diferentes definem escopos diferentes.

A partir versão 1.2, NHibernate adicionou o método


ISessionFactory.GetCurrentSession().O processo por trás do método
SessionFactory.GetCurrentSession() é plugáveis. Com isso, uma nova interface
(NHibernate.Context.ICurrentSessionContext) e um novo parâmetro de configuração
(HIBERNATE.current_session_context_class) foram adicionados para possibilitar a
compatibilidade do contexto e do escopo na definição de sessões correntes.

6. Classes Persistentes
As classes persistentes de uma aplicação são aquelas que implementam as
entidades de domínio de negócio. O NHibernate apresenta um melhor desempenho se
trabalhar com algumas regras simples conhecidas como Plain Old CLR Object (POCO). A
Listagem abaixo
apresenta a classe Cat representando uma classe POCO.

public class Cat


{
private long _id; // identifier
private string _name;
private DateTime _birthdate;
private Cat _mate;

public long Id
{
get { return _id; }
set { _id = value; }
}

public string Name


{
get { return _name; }
set { _name = value; }
}

public DateTime Birthdate


{
get { return _birthdate; }
set { _birthdate = value; }
}

NHibernate – Persistência de Dados em Plataforma .NET 12


6.1 Considerações:
• O NHibernate requer que toda classe persistente possua um construtor
padrão sem argumentos, assim, o NHibernate pode instanciá-las
simplesmente chamando Activator.CreateInstance ();

• O Nhibernate persiste propriedades usando os método get e set. Observe que a


classe Pessoa apresenta métodos setters e getters para acessar ou retornar todos os
seus atributos. Propriedades não pode ser declaradas como Públicas. O NHibernate
pode persistir uma propriedade com uma visibilidade internal, protected, protected
internal ou private.

• A classe Cat possui um atributo id que é o seu identificador


único. É importante que, ao utilizar NHibernate, todos os objetos
persistentes possuam um identificador e que eles sejam
independentes da lógica de negócio da aplicação. Porém, você pode deixá-lo de fora
e deixar que o Nhibernate acompanhe os objetos internamente. Além do mais ,
algumas funcionalidades só estão disponíveis para classes que declaram uma
propriedade identificadora:

• Atualização em cascata
Isession.SaveUpdate()

6.2 Identidade/Igualdade de Obetos (Equals () e GetHashCode () )

No Nhibernate o conceito de igualdade entre objetos é diferente. Dois objetos, por


exemplo, duas Strings, podem ter o mesmo conteúdo, mas verificar se as duas são iguais
utilizando o operador ==, pode retornar um resultado errado, pois o operado == implicará
em uma verificação da posição de memória e não do conteúdo. Se você tiver a intenção de
unir objetos de classes persistentes deverá usar os métodos Equals() e GetHashCode(), (por
exemplo, um ISet). Incluindo o conceito de persistência, passa a existir um novo conceito
de identidade, a identidade de banco de dados. Dois objetos armazenados em um
banco de dados são idênticos se forem mapeados em uma mesma linha da tabela.

Isso só se aplica se esses objetos são carregados em dois diferentes ISession s. Como o
NHibernate garante apenas a identidade (a == b, a implementação do padrão Equals ())
dentro de uma única ISession!

Mesmo que os dois objetos a e b estejam na mesma coluna do banco de dados (eles têm o
seu identificador com mesmo valor de chave primária), não podemos garantir que eles são
instância do mesmo objeto fora de um contexto particular ISession.

A maneira mais óbvia de implementar Equals () / GetHashCode()é comparando o valor do


identificador de ambos os objetos. Se o valor é o mesmo, ambos devem estar na mesma

NHibernate – Persistência de Dados em Plataforma .NET 13


coluna do banco de dados, por isso, são iguais (se ambos são adicionados a um ISet, vamos
ter apenas um elemento no ISet). Infelizmente, não podemos usar essa abordagem. O
NHibernate só irá atribuir valores aos identificador dos objetos que são persistentes, uma
instância recém-criada não terá qualquer valor no identificador! Recomendamos a
execução Equals() e GetHashCode() usando Business key equality.

Business key equality (igualar chaves de negócios) significa que o método


Equals()compara apenas as unidades que formam o chave de negócio. Uma chave que
permitiria identificar o nosso exemplo, no mundo real (uma chave candidata natural):

public class Cat


{

...
public override bool Equals(object other)
{
if (this == other) return true;

Cat cat = other as Cat;


if (cat == null) return false; // null or not a cat

if (Name != cat.Name) return false;


if (!Birthday.Equals(cat.Birthday)) return false;

return true;
}

public override int GetHashCode()


{
unchecked
{
int result;
result = Name.GetHashCode();
result = 29 * result + Birthday.GetHashCode();
return result;
}
}

Tenha em mente que a nossa chave candidata (neste caso, um composto de nome e data de
aniversário) tem de ser válido apenas para uma determinada operação de comparação
(talvez apenas em caso de utilização única). Não precisamos critérios de estabilidade,
normalmente se aplica a uma chave primária real!

NHibernate – Persistência de Dados em Plataforma .NET 14


6.3 Escolhendo Chaves Primárias
Um passo importante ao utilizar o NHibernate é informá-lo sobre a estratégia
utilizada para a geração de chaves primárias.
Uma chave candidata é uma coluna ou um conjunto de colunas que
identifica unicamente uma linha de uma tabela do banco de dados. Ela deve
satisfazer as seguintes propriedades:

• Única;
• Nunca ser nula;
• Constante.

Uma única tabela pode ter várias colunas ou combinações de colunas que
satisfazem essas propriedades. Se a tabela possui um único atributo que a
identifique, ele é por definição a sua chave primária. Se possuir várias chaves
candidatas, uma deve ser escolhida para representar a chave primária e as demais
serem definidas como chaves únicas.
Muitas aplicações utilizam como chaves primárias chaves naturais, ou seja,
que têm significados de negócio. Essa estratégia pode não ser muito boa em longo
prazo, já que uma chave primária adequada deve ser constante, única e não nula.
Dessa forma, se for desejado que a chave primária da tabela Cat seja uma
outra ao invés do id, podem surgir problemas já que provavelmente o id deve
ser referenciado em outras tabelas. Um problema que poderia acontecer seria a
remoção do id da tabela.

7. Configurando o NHibernate
Pode-se configurar o NHibernate de três maneiras distintas:

• Instanciar um objeto de configuração


(NHibernate.Cfg.Configuration) e inserir suas propriedades
programaticamente;
• Usar um arquivo .properties com as suas configurações e indicar as
classes mapeadas programaticamente;
• Usar um arquivo XML (hibernate.cfg.xml) com as propriedades de
inicialização e os caminhos das classes mapeadas.

Será apresentada a configuração a partir do arquivo hibernate.cfg.xml.


Um exemplo deste arquivo de configuração pode ser visto na Listagem abaixo. Vários
parâmetros podem ser configurados. Basicamente, deve-se configurar:
• A URL de conexão com o banco de dados;
• Usuário e senha do banco de dados;
• Números máximo e mínimo de conexões no pool;
• Dialeto.

NHibernate – Persistência de Dados em Plataforma .NET 15


<?xml version='1.0' encoding='utf-8'?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.0">

<!-- an ISessionFactory instance -->


<session-factory>

<!-- properties -->


<property
name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property
>
<property
name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="connection.connection_string">Server=localhost;initial
catalog=nhibernate;User Id=;Password=</property>
<property name="show_sql">false</property>
<property name="dialect">NHibernate.Dialect.MsSql2000Dialect</property>
<property name="use_outer_join">true</property>

<!-- mapping files -->


<mapping resource="NHibernate.Auction.Item.hbm.xml"
assembly="NHibernate.Auction" />
<mapping resource="NHibernate.Auction.Bid.hbm.xml"
assembly="NHibernate.Auction" />

</session-factory>

</hibernate-configuration>
Arquivo de Configuração hibernate.cfg.xml

• dialect: implementação do dialeto SQL específico do banco


de dados a ser utilizado. Usado para identificar as particularidades do
banco de dados;
• connection.driver_class: nome da classe do driver JDBC
do banco de dados que está sendo utilizado;
connection_string: é a seqüência de caracteres utilizados para obter conexão de
conexão;
• show_sql: utilizado para definir se os SQL’s gerados pelo NHibernate devem ou
não ser exibidos (true | false).
• use_outer_join: Permite buscar associação externa. Substituído, use
max_fetch_depth.

NHibernate – Persistência de Dados em Plataforma .NET 16


8. Manipulando Objetos Persistentes

O NHibernate utiliza objetos ISession para persistir e recuperar objetos. Um


objeto ISession pode ser considerado como uma sessão de comunicação com o
banco de dados através de uma conexão ADO.NET.

8.1 Criando um Objeto persistente

Um objeto (exemplo entidade), é transitória ou persistente, no que diz respeito a um


ISession particular. Objetos novos instanciados são, naturalmente, transitórios. A sessão
oferece serviços para a persistência de casos transitórios:

DomesticCat fritz = new DomesticCat();


fritz.Color = Color.Ginger;
fritz.Sex = 'M';
fritz.Name = "Fritz";
long generatedId = (long) sess.Save(fritz);

DomesticCat pk = new DomesticCat();


pk.Color = Color.Tabby;
pk.Sex = 'F';
pk.Name = "PK";
pk.Kittens = new HashSet();
pk.AddKitten(fritz);
sess.Save( pk, 1234L );

O single-argument Save() gera e atribui um identificador único para fritz. O two-argument


cria formulários na tentativa de persistir pk usando o identificador dado.

Objetos Associados podem ser persistidos em qualquer ordem que você desejar a menos
que você tenha uma restrição NOT NULL mediante uma coluna chave estrangeira. Não há
qualquer risco de violar uma restrição da chave estrangeira. No entanto, você poderá violar
um constrangimento NOT NULL se você salvar os objetos de forma incorreta.

8.2 Carregando um objeto

Os métodos Load() da ISession oferecem uma maneira de recuperar uma instância


persistente se você já souber seu identificador. Uma versão pegará um objeto da classe e
carregará o estado em um objeto recém-instanciado. A segunda versão permite que você
forneça uma instância no qual o estado será carregado. O formulário que usa uma instância
é útil apenas em circunstâncias especiais (instância DIY pooling etc).

Cat fritz = (Cat) sess.Load(typeof(Cat), generatedId);

NHibernate – Persistência de Dados em Plataforma .NET 17


long pkId = 1234;
DomesticCat pk = (DomesticCat) sess.Load( typeof(Cat), pkId );

Cat cat = new DomesticCat();


// load pk's state into cat
sess.Load( cat, pkId );
ISet kittens = cat.Kittens;

Observe que o Load() apresentará uma exceção irrecuperável se não houver nenhuma linha
do banco de dados correspondente. Se a classe é mapeada com um proxy, o Load() retorna
um objeto proxy não inicializado e não atualizado do banco de dados até que você chame
um método do objeto. Esse comportamento é muito útil se você deseja criar uma associação
para um objeto sem realmente carregá-lo do banco de dados.

Se você não tiver certeza de que existe uma linha coincidente, você deve usar o método
Get(), que atinge o banco de dados imediatamente e retorna nulo se não houver nenhuma
linha correspondente.

Cat cat = (Cat) sess.Get(typeof(Cat), id);


if (cat==null) {
cat = new Cat();
sess.Save(cat, id);
}
return cat;

Você também pode carregar um objetos usando um SQL SELECT ... FOR UPDATE

Cat cat = (Cat) sess.Get(typeof(Cat), id, LockMode.Upgrade);

Observe que quaisquer instâncias associadas ou coleções contidas não são selecionadas
FOR UPDATE.

8.3 Busca

Se você não sabe o qual o identificador(s) do objeto(s) que você está procurando, use o
método Find()da ISession. O NHibernate suporta uma simples, mas poderosa linguagem
orientada a objeto para consulta.

NHibernate – Persistência de Dados em Plataforma .NET 18


IList cats = sess.Find(
"from Cat as cat where cat.Birthdate = ?",
date,
NHibernateUtil.Date
);

IList mates = sess.Find(


"select mate from Cat as cat join cat.Mate as mate " +
"where cat.name = ?",
name,
NHibernateUtil.String
);

IList cats = sess.Find( "from Cat as cat where cat.Mate.Birthdate is null" );

IList moreCats = sess.Find(


"from Cat as cat where " +
"cat.Name = 'Fritz' or cat.id = ? or cat.id = ?",
new object[] { id1, id2 },
new IType[] { NHibernateUtil.Int64, NHibernateUtil.Int64 }
);

IList mates = sess.Find(


"from Cat as cat where cat.Mate = ?",
izi,
NHibernateUtil.Entity(typeof(Cat))
);

IList problems = sess.Find(


"from GoldFish as fish " +
"where fish.Birthday > fish.Deceased or fish.Birthday is null"
);

• Criterias Queries

HQL é extremamente poderoso, mas algumas pessoas preferem criar consultas


dinamicamente usando um objeto orientado a API, em vez de incorporar as seqüências de
caracteres em seus códigos .NET. Para essas pessoas, o NHibernate fornece uma consulta
ICriteria intuitiva API.

ICriteria crit = session.CreateCriteria(typeof(Cat));


crit.Add( Expression.Eq("color", Eg.Color.Black) );
crit.SetMaxResults(10);
IList cats = crit.List();

NHibernate – Persistência de Dados em Plataforma .NET 19


• SQL Native

Você pode expressar uma consulta no SQL usando CreateSQLQuery(). Você deve colocar
os aliases SQL entre chaves.

IList cats = session.CreateSQLQuery(


"SELECT {cat.*} FROM CAT {cat} WHERE ROWNUM<10",
"cat", typeof(Cat)).List();

IList cats = session.CreateSQLQuery(


"SELECT {cat}.ID AS {cat.Id}, {cat}.SEX AS {cat.Sex}, " +
"{cat}.MATE AS {cat.Mate}, {cat}.SUBCLASS AS {cat.class}, ... " +
"FROM CAT {cat} WHERE ROWNUM<10",
"cat",
typeof(Cat)
).List()

As consultas SQL podem conter parâmetros nomeados e posicionais, exatamente como


consultas NHibernate.

8.4 Atualizando objetos

Você pode Incluir um objeto na tabela caso ele ainda não exista
(seja transiente) ou atualizar o objeto caso ele já
exista (seja persistente) utilizando o método SaveOrUpdate ().

// in the first session


Cat cat = (Cat) firstSession.Load(typeof(Cat), catID);

// in a higher tier of the application


Cat mate = new Cat();
cat.Mate = mate;
// later, in a new session
secondSession.SaveOrUpdate(cat); // update existing state (cat has a non-null id)
secondSession.SaveOrUpdate(mate); // save the new instance (mate has a null id)

8.5 Deletando um objeto

O ISession.Delete() irá remover um objeto do banco de dados.

sess.Delete(cat);

NHibernate – Persistência de Dados em Plataforma .NET 20


8.6 Flush

De tempos em tempos a ISession irá executar os comandos SQL necessários para


sincronizar a conexão de Estado do ADO.NET com o estado dos objetos, em memória. Este
processo, flush, ocorre por omissão, nos seguintes pontos:

• a partir de algumas invocações de Find() ou Enumerable()


• a partir do NHibernate.ITransaction.Commit ()
• a partir da ISession.Flush ()

As instruções SQL são emitidas na seguinte ordem:

• todas as inserções na entidade, na mesma ordem que os objetos correspondentes


foram salvos usando Save()
• todas as atualizações na entidade
• todas as coleções deletas
• todosos elementos de coleções delatados, atualizados e inseridos
• todas as inserções
• todas as entidades delatadas, na mesma ordem que os objetos correspondentes
foram delatados usando ISession.Delete ()

sess = sf.OpenSession();
ITransaction tx = sess.BeginTransaction();
sess.FlushMode = FlushMode.Commit; //allow queries to return stale state
Cat izi = (Cat) sess.Load(typeof(Cat), id);
izi.Name = "iznizi";
// execute some queries....
sess.Find("from Cat as cat left outer join cat.Kittens kitten");
//change to izi is not flushed!
...
tx.Commit(); //flush occurs

8.7 Encerrar uma sessão

Terminar uma sessão envolve quatro fases distintas:

• Liberar a sessão
• confirmar a transação
• fechar a sessão
• tratar exceções

NHibernate – Persistência de Dados em Plataforma .NET 21


ISession sess = factory.openSession();
try
{
// do some work
...
sess.Flush();
currentTransaction.Commit();
}
catch (Exception e)
{
currentTransaction.Rollback();
throw;
}
finally
{
sess.Close();
}

9. Mapeando O/R básico


9.1. Declaração de Mapeamento

Os mapeamentos objeto / relacional são definidos em um documento XML. O documento


mapeado é projetado para ser legível e de mãos-editáveis. A linguagem de mapeamento é
objeto-centric, o que significa que os mapeamentos são construídos em torno de
declarações de classes persistentes, e não declarações de tabelas.

Note que, apesar de muitos usuários NHibernate optarem por definir mapeamentos XML à
mão, existem uma série de ferramentas para gerar o mapeamento do documento, incluindo
a biblioteca NHibernate.Mapping.Attributes e de vários modelos baseados em geradores de
código (CodeSmith, MyGeneration).

NHibernate – Persistência de Dados em Plataforma .NET 22


Vamos iniciar com um exemplo de mapeamento:

<?xml version="1.0"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Eg"
namespace="Eg">

<class name="Cat" table="CATS" discriminator-value="C">


<id name="Id" column="uid" type="Int64">
<generator class="hilo"/>
</id>
<discriminator column="subclass" type="Char"/>
<property name="BirthDate" type="Date"/>
<property name="Color" not-null="true"/>
<property name="Sex" not-null="true" update="false"/>
<property name="Weight"/>
<many-to-one name="Mate" column="mate_id"/>
<set name="Kittens">
<key column="mother_id"/>
<one-to-many class="Cat"/>
</set>
<subclass name="DomesticCat" discriminator-value="D">
<property name="Name" type="String"/>
</subclass>
</class>

<class name="Dog">
<!-- mapping for Dog could go here -->
</class>

</hibernate-mapping>

NHibernate – Persistência de Dados em Plataforma .NET 23


Vamos agora discutir o conteúdo do documento de mapeamento. Vamos apenas descrever
elementos e atributos do documento que são utilizados por NHibernate no decorrer do
processo. O documento de mapeamento contém ainda alguns atributos opcionais extras e
elementos que afetam schemas de banco de dados exportados pelo schema da ferramenta de
exportação. not-null (Por exemplo, o atributo not-null.)

9.1.1. XML Namespace

Todos os mapeamentos XML devem declarar o namespace XML mostrado. A atual


definição do schema pode ser encontrada no arquivo src\nhibernate-Mapping.xsd na
distribuição de NHibernate.

9.1.2. Hibernate-mapping

Este elemento tem vários atributos opcionais. O atributo schema especifica a que tabelas
referenciadas por este mapeamento pertencem o esquema nomeado. Se especificado, os
nomes das tabelas serão qualificadas pelo nome dado schema. Se faltar, o nome da tabela
será incondicional. O atributo default-cascade especifica qual o estilo de cascata que deve
ser assumida pelas propriedades e coleções que não especificam um atributo cascade. O
atributo auto-import permite-nos utilizar incondicionalmente nomes de classe na consulta,
por default. Os atributos assembly e namespace especificam o assembly onde classes
persistentes estão localizadas e namespace que foram declaradas.

<hibernate-mapping
schema="schemaName" (1)
default-cascade="none|save-update" (2)
auto-import="true|false" (3)
assembly="Eg" (4)
namespace="Eg" (5)
/>

(1) schema (opcional): O nome do schema de um banco de dados.


(2) default-cascade (opcional - defaults para none): Um estilo cascata padrão.
(3) O auto-import (opcional - defaults para true): Especifica se podemos utilizar
incondicionalmente nomes de classes (neste mapeamento de classes) na consulta.
assembly e namespace (opcional): Especificar assembly e namespace para assumir
(4) incondicionalmente denominações para a classe no documento de mapeamento .
(5)

NHibernate – Persistência de Dados em Plataforma .NET 24


Se você não estiver usando atributos assembly e namespace, você precisará especificar
nomes de classes totalmente qualificados, incluindo o nome do assembly que as classes são
declaradas.

Se você tem duas classes persistentes com o mesmo nome (incondicional), você deve
definir auto-import = "false". O NHibernate irá lançar uma exceção, se você tentar atribuir
duas classes para os mesmos nomes "imported".

9.1.3. Classes

Você pode declarar uma classe persistente utilizando o elemento class:

<class
name="ClassName"
table="tableName"
discriminator-value="discriminator_value"
mutable="true|false"
schema="owner"
proxy="ProxyInterface"
dynamic-update="true|false"
dynamic-insert="true|false"
select-before-update="true|false"
polymorphism="implicit|explicit"
where="arbitrary sql where condition"
persister="PersisterClass"
batch-size="N"
optimistic-lock="none|version|dirty|all"
lazy="true|false"
/>

Ele é perfeitamente aceitável pela classe persistente nomeada para ser uma interface .

9.1.4. Id

Classes mapeadas devem declarar uma coluna chave primária na tabela do banco de dados.
A maioria das classes também terá uma propriedade que contém o identificador exclusivo
de uma instância. O elemento <id> define o mapeamento a partir dessa propriedade para a
coluna da chave primária.

<id
name="PropertyName" (1)
type="typename" (2)
column="column_name" (3)
unsaved-value="any|none|null|id_value" (4)
access="field|property|nosetter|ClassName(5)">
<generator class="generatorClass"/>
</id>

NHibernate – Persistência de Dados em Plataforma .NET 25


(1) Name (opcional): O nome da propriedade do identificador.
(2) Type (opcional): Um nome que indica o tipo NHibernate.
(3) column (opcional - defaults para a propriedade name): O nome da coluna chave
primária.
(4) unsaved-value (opcional - defaults para um valor "sensible"):Valor da propriedade do
identificador que indica que uma instância foi recém instanciada (não salvas),
distinguindo-a a partir de casos transitórios que foram salvas ou carregadas em uma
sessão anterior.
(5) access (opcional - defaults de propriedade): O NHibernate deve usar uma estratégia
para acessar o valor da propriedade.

Se o atributo name estiver faltando, presume-se que a classe não tem propriedade no
identificador .

O atributo unsaved-value quase nunca é necessário no NHibernate 1.0.

Há uma declaração alternativa <composite-id> para permitir o acesso a dados com chaves
compostas. Nós desaconselhamos seu uso para qualquer outra coisa.

9.1.4.1. Gerador

O elemento <generator> é usado para gerar identificadores originais para insatancias da


classe persistente. Se alguns parâmetros são exigidos para configurar ou para inicializar a
instância do gerador é utilizado o elemento <param>.

<id name="Id" type="Int64" column="uid" unsaved-value="0"> <generator


class="NHibernate.Id.TableHiLoGenerator"> <param name="table"> uid_table </ param>
<param name="column"> next_hi_value_column </ param> </ gerador> </ id>

Todos os geradores implementam uma interface NHibernate.Id.IIdentifierGenerator. Esta


é uma interface muito simples; algumas aplicações podem optar por prestar suas próprias
implementações especializadas. No entanto, o NHibernate prevê uma série
implementações internas.

incremento
gera os identificadores de qualquer tipo integral que são originados somente quando
nenhum outro processo está introduzindo dados na mesma tabela. Não usar em um cluster.

NHibernate – Persistência de Dados em Plataforma .NET 26


Identidade

Suporta colunas identidade em DB2, MySQL, usuários do MS SQL e Sybase. O


identificador retornado pela base de dados é convertido ao tipo de propriedade
utilizando Convert.ChangeType. Qualquer tipo de propriedade integral é suportado.

Sequência

usa uma seqüência em DB2, em PostgreSQL, em Oracle ou em um gerador em


Firebird. O identificador retornado pelo banco de dados é convertido em um tipo de
propriedade usando o Convert.ChangeType. Qualquer tipo de propriedade integral é
suportado.

Hilo

usa um algoritmo hi/lo de forma eficiente para gerar identificadores integrantes de


qualquer tipo, dada uma tabela e coluna (por padrão hibernate_unique_key e
next_hi respectivamente) como uma fonte de valores hi. O algoritmo hi / lo gera
identificadores que são exclusivos apenas para um determinado banco de dados.
Não utilize este gerador em conxeções fornecidas pelo usuário.

seqhilo

usa um algoritmo hi/lo para gerar identificadores eficientemente de qualquer tipo


integral, dado uma seqüência nomeada da base de dados.

uuid.hex

usa o método System.Guid e o ToString(string format) para gerar identificadores


tipo string .O tamanho da string retornada depende do formato configurado.

uuid.string

utiliza uma nova System.Guid para criar um byte [] que é convertido para uma
string.

ORIENT

utiliza uma nova System.Guid como o identificador.

guid.comb

NHibernate – Persistência de Dados em Plataforma .NET 27


usa o algoritmo para gerar uma nova System.Guid. Descrito por Jimmy Nilsson no
artigo http://www.informit.com/articles/article.asp?p=25862.

nativas

escolhe identity, ou sequence, ou Hilo dependendo dos recursos do banco de dados


subjacente.

atribuídas

permite que a aplicação atribua um identificador para o objeto antes de


salvar(Save()).

estrangeira

usa o identificador de um outro objeto associado. Normalmente utilizada em


conjunto com uma chave primária numa associação <one-to-one>.

9.1.5. Composite-ID

<composite-id
name="PropertyName"
class="ClassName"
unsaved-value="any|none"
access="field|property|nosetter|ClassName">

<key-property name="PropertyName" type="typename" column="column_name"/>


<key-many-to-one name="PropertyName class="ClassName"
column="column_name"/>
......
</composite-id>

Para uma tabela com uma chave composta, você pode mapear várias propriedades da
classe como propriedades do identificador. O elemento <composite-id> aceita a
propriedade de mapeamentos <key-property> e mapeamentos como elementos filhos <key-
many-to-one>.

<composite-id>
<key-property name="MedicareNumber"/>
<key-property name="Dependent"/>
</ composite-id>

NHibernate – Persistência de Dados em Plataforma .NET 28


Sua classe persistente deve ter prioridade sobre Equals () e GetHashCode () para executar
igualdade de identificador composto. Também deve ser Serializable.

Infelizmente, esta abordagem de identificadores composto significa que um objeto


persistente é o seu próprio identificador. Não existe nenhum "handler(manipulador)"
conveniente que não seja o próprio objeto. Você deve instanciar uma instância da classe
persistente e povoar as suas próprias propriedades do identificador antes de associar ao
estado de persistência load() a uma chave composta.

• name (opcional, necessário para esta abordagem): Um tipo imóvel de componente


que detém o identificadores compostos.
• access (opcional - defaults de property): A estratégia que o NHibernate usa para
acessar valor da propriedade.
• class (opcional ): Classe componente utilizada como um identificador de
composição.

9.1.6. Discriminador

O elemento <discriminator> é necessário para persistências polimórficas utilizando a


tabela por classe de hierarquia que mapeia a estratégia e declara a coluna discriminador da
tabela. A coluna do discriminador contém os valores do marcador que dizem a camada de
persistência que instanciou uma subclasse de uma determinada linha. Um conjunto restrito
de tipos pode ser utilizado: String, Char, Int32, Byte, Short, booleanos, YesNo, TrueFalse.

<discriminator
column="discriminator_column" (1)
type="discriminator_type" (2)
force="true|false" (3)
insert="true|false" (4)
formula="arbitrary SQL expressi(5)on"
/>

(1) Column (opcional - defaults para class) o nome da coluna discriminador.


(2) type (opcional - defaults para String) um nome que indica o tipo NHibernate
(3) force (opcional - defaults para false) "força" o NHibernate especificar os valores
permitidos do discriminador mesmo quando estiver todas as instâncias da classe de
raiz estiverem recuperadas.
(4) insert (opcional - defaults para true) é estabelecido o valor false se sua coluna
discriminadora também fizer parte de um mapeamento de identificadores compostos.

(5) formula (opcional) uma expressão SQL arbitraria é executado quando um tipo tem de
ser avaliada. Permite conteúdo baseado em discriminação.

NHibernate – Persistência de Dados em Plataforma .NET 29


Valores reais da coluna discriminadora são especificados pelo valor do atributo
discriminator-value e pelos elementos <class> e <subclass>.

O atributo force é (apenas) útil se a tabela contém linhas com valores de discriminadores
"extras" que não são mapeados para uma classe persistente. Isto normalmente não vai ser o
caso. Utilizando o atributo formula você pode declarar expressões arbitrárias SQL que
serão utilizados para avaliar o tipo de uma linha: <discriminator formula="case where
CLASS_TYPE in ('a','b','c'),then 0 else 1 end " type="Int32"/>

9.1.7. Propriedade

O elemento <property> declara uma propriedade da classe persistente .

<property
name="propertyName" (1)
column="column_name" (2)
type="typename" (3)
update="true|false" (4)
insert="true|false" (4)
formula="arbitrary SQL expression" (5)
access="field|property|ClassName" (6)
optimistic-lock="true|false" (7)
generated="never|insert|always" (8)
/>

(1) name: o nome da propriedade de sua classe.


(2) column (opcional - defaults para a propriedade nome): o nome da coluna do banco de
dados da tabela mapeada.
(3) type (opcional): um nome que indica o tipo NHibernate.
(4) update, insert (opcional - defaults para true): especifica quais colunas mapeadas
devem ser incluídas no SQL UPDATE e / ou declarações INSERT.
(5) formula (opcional): expressão SQL que define o valor de uma propriedade computed.
As propriedades computadas não têm um mapeamento de colunas próprias.
(6) access (opcional - defaults para property): Deve ser usada para acessar a valor da
propriedade.
(7) otimista-lock (opcional - defaults a verdade): Especifica quais atualizações para esta
propriedade exigem ou não uma aquisição de bloqueio otimista.
(8) generated (opcional - defaults para nunca): Especifica que valor da propriedade está
na verdade sendo gerado pela banco de dados.

NHibernate – Persistência de Dados em Plataforma .NET 30


typename poderia ser:

• O nome de tipo básico NHibernate (por exemplo, Int32, String, char, DateTime,
timestamp, Single, Byte [], Object, ...).
• O nome de um tipo .NET com um padrão básico de tipo (por exemplo,
System.Int16, System.Single, System.Char, System.String, System.DateTime,
System.Byte [], ...).
• O nome de um tipo enumeração (por exemplo, Eg.Color, por exemplo).
• O nome de um tipo SERIALIZABLE. NET.
• O nome da classe de tipos personalizados (ex. Illflow.Type.MyCustomType).

Note que você tem que especificar nomes assembly-qualified para todos os tipos, com
exceção dos tipos de atributos (a menos que você defina os atributos assembly e/ou
namespace do elemento <hibernate-mapping> ).

O NHibernate suporta tipos . NET 2.0 Nullabels.

O atributo access permite que você controle como o NHibernate terá acesso ao valor da
propriedade no momento da execução. O valor do atributo access deve ser um texto
formatado como access-strategy.naming-strategy. A .naming-strategy nem sempre é
necessária.

Tabela: Estratégias de Acesso

Nome da
estratégia de Descrição
acesso
Implementação Default. O NHibernate usa get/set ajustado à propriedade.
Nenhum estratégia de nomeação deve ser usada como a estratégia de
property acesso porque o valor do atributo name é o nome da propriedade.

O NHibernate acessará o campo diretamente. O NHibernate usa o valor do


atributo name como o nome do campo. Isto pode ser usado quando os
field métodos get e set de uma propriedade contêm ações extras que você não
quer que ocorram quando o NHibernate estiver preenchendo ou lendo o
objeto.
O NHibernate acessará o campo diretamente ao definir o valor e usará a
Nosetter
propriedade obtendo o valor.
Se as estratégias de acesso construídas pelo Nhibernate não forem
adequadas para a situação, você poderá criar sua própria estratégia através
ClassName da interface de implementação NHibernate.Property.IPropertyAccessor. O
valor do atributo de acesso dever ser um nome assembly-qualified que
pode ser carregado com o Activator.CreateInstance(string

NHibernate – Persistência de Dados em Plataforma .NET 31


Nome da
estratégia de Descrição
acesso
assemblyQualifiedName).

Tabela: Naming Strategies

Naming Strategy
Descrição
Name
O atributo name é convertido para camel case para localizar o campo
camelcase
usando o campo foo <property name="Foo" ... >.
O atributo name é convertido para camel case e prefixado com um
camelcase-
símbolo underline usando o campo _foo para localizar o <property
underscore
name="Foo" ... >.
O atributo name é convertido para lowercase para encontrar o campo
lowercase
foobar usando <property name="FooBar" ... >
lowercase- O atributo name é convertido para lowercase para encontrar o campo
underscore foobar usando <property name="FooBar" ... >
O atributo name é convertido para pascalcase e prefixado com um
pascalcase-
underline para encontrar o campo foobar usando <property
underscore
name="FooBar" ... >.
O atributo name é prefixado com o caracter m para o campo <property
pascalcase-m
name="Foo" ... > usando campo mFoo .
pascalcase-m- O atributo name é prefixado com o caracter m e um underline para
underscore encontar o campo <property name="Foo" ... > usando campo m_Foo .

9.1.8. many-to-one

Uma associação comum para outra classe persistente é declarada usando um elemento de
many-to-one. O modelo relacional é uma associação many-to-one. (É apenas uma
referência do objeto.)

<many-to-one
name="PropertyName"
column="column_name"
class="ClassName"
cascade="all|none|save-update|delete"
fetch="join|select"

update="true|false"

NHibernate – Persistência de Dados em Plataforma .NET 32


insert="true|false"

property-ref="PropertyNameFromAssociatedClass"

access="field|property|nosetter|ClassName"
unique="true|false"
optimistic-lock="true|false"
not-found="ignore|exception"
/>

9.1.9. one-to-one
Uma associação um-para-um para outra classe persistente é declarada usando um elemento
de um-para-um

<one-to-one
name="PropertyName"
class="ClassName"
cascade="all|none|save-update|delete"
constrained="true|false"
fetch="join|select"
property-ref="PropertyNameFromAssociatedClass"
access="field|property|nosetter|ClassName"
/>

Há duas variedades de associação um-para-um:

• associação de chaves primárias


• associações exclusivas de chave externas

Para associações de chave primárias não é necessário uma coluna extra na tabela; se duas
linhas estão relacionadas pela associação, então, as linhas das duas tabelas compartilham o
mesmo valor de chave-primária. Portanto, se você desejar ter dois objetos se relacionando
por uma associação de chave primária, você deve certificar-se de que eles recebem o
mesmo valor identificador!

Para uma associação de chave primária, adicione os seguintes mapeamentos para Employee
e Person, respectivamente.

<one-to-one name="Person" class="Person"/>


<one-to-one name="Employee" class="Employee" constrained="true"/>

NHibernate – Persistência de Dados em Plataforma .NET 33


9.1.10. subclass
Finalmente, a persistência polimórfica requer a declaração de cada subclasse da classe
persistente raiz. Para a estratégia de mapeamento table-per-class-hierarchy (recomendável)
é usada a declaração <subclass>.

<subclass
name="ClassName" (1)
discriminator-value="discriminator_value" (2)
proxy="ProxyInterface" (3)
lazy="true|false" (4)
dynamic-update="true|false"
dynamic-insert="true|false">

<property .... />


.....
</subclass>

9.2. NHibernate Types

9.2.1. Tipos Básicos

Os tipos básicos podem ser categorizados aproximadamente em três grupos - tipos


System.ValueType, tipos System.Object e tipos System.Object de objetos grandes. Assim
como os tipos .NET, colunas para tipos System.ValueType não podem armazenar valores
null . Tipos System.Object podem armazenar valores nulos.

Tabela : Tipos de mapeamento System.ValueType

NHibernate
.NET Type Database Type Remarks
Type
DbType.AnsiStringFixedLeng
AnsiChar System.Char
th - 1 char
Default quando o tipo
Boolean System.Boolean DbType.Boolean do atributo não for
especificado.
Default quando o tipo
Byte System.Byte DbType.Byte do atributo não for
especificado.
Char System.Char DbType.StringFixedLength - Default quando o tipo

NHibernate – Persistência de Dados em Plataforma .NET 34


NHibernate
.NET Type Database Type Remarks
Type
1 char do atributo não for
especificado.
Default quando o tipo
System.DateTim DbType.DateTime - ignores
DateTime do atributo não for
e the milliseconds
especificado..
Default quando o tipo
Decimal System.Decimal DbType.Decimal do atributo não for
especificado..
Default quando o tipo
Double System.Double DbType.Double do atributo não for
especificado.
Default quando o tipo
Guid System.Guid DbType.Guid do atributo não for
especificado..
Default quando o tipo
Int16 System.Int16 DbType.Int16 do atributo não for
especificado.
Default quando o tipo
Int32 System.Int32 DbType.Int32 do atributo não for
especificado.
Default quando o tipo
Int64 System.Int64 DbType.Int64 do atributo não for
especificado.
Do not specify
type="PersistentEnu
m" in the mapping.
Instead specify the
Assembly Qualified
Name of the Enum or
PersistentEnu The DbType for the
A System.Enum let NHibernate use
m underlying value.
Reflection to "guess"
the Type. The
UnderlyingType of
the Enum is used to
determine the correct
DbType .
Default quando o tipo
Single System.Single DbType.Single
do atributo não for

NHibernate – Persistência de Dados em Plataforma .NET 35


NHibernate
.NET Type Database Type Remarks
Type
especificado.
System.DateTim type="Ticks" deve ser
Ticks DbType.Int64
e especificado
Default quando o tipo
System.TimeSpa
TimeSpan DbType.Int64 do atributo não for
n
especificado.
System.DateTim DbType.DateTime - as type="Timestamp"
Timestamp
e specific as database supports. deve ser especificado.
DbType.AnsiStringFixedLeng type="TrueFalse"
TrueFalse System.Boolean
th - 1 char either 'T' or 'F' deve ser especificado.
DbType.AnsiStringFixedLeng type="YesNo" deve
YesNo System.Boolean
th - 1 char either 'Y' or 'N' ser especificado

Tabela Tipos de mapeamento System.Object

NHibernate
.NET Type Database Type Remarks
Type
type="AnsiString"
AnsiString System.String DbType.AnsiString deve ser
especificado
Default quando o
DbType.String - 5 tipo do atributo
CultureInfo System.Globalization.CultureInfo
chars for culture não for
especificado.
Default quando o
tipo do atributo
Binary System.Byte[] DbType.Binary
não for
especificado.
Default quando o
DbType.String
tipo do atributo
Tipo System.Type holding Assembly
não for
Qualified Name.
especificado.
Default quando o
tipo do atributo
String System.String DbType.String
não for
especificado

NHibernate – Persistência de Dados em Plataforma .NET 36


Tabela: Tipos de mapeamento Large Object

NHibernate
.NET Type Database Type Remarks
Type
type="StringClob" deve ser
StringClob System.String DbType.String especificada. O campo inteiro é
lido na memória
type="BinaryBlob" deve ser
BinaryBlob System.Byte[] DbType.Binary especificada. O campo inteiro é
lido na memória
type="Serializable" Deve ser
Any System.Object that especificado. Este é o tipo de
Serializable is marked with DbType.Binary fallback se nenhum tipo
SerializableAttribute. NHibernate pode ser
encontrado para a propriedade.

10. Mapeando coleções


O NHibernate exige que campos collections-value persistentes sejam declarados como um
tipo de interface, por exemplo:

public class Product


{
private string serialNumber;
private ISet parts = new HashedSet();

public ISet Parts


{
get { return parts; }
set { parts = value; }
}

public string SerialNumber


{
get { return serialNumber; }
set { serialNumber = value; }
}
}

NHibernate – Persistência de Dados em Plataforma .NET 37


A interface real pode ser Iesi.Collections.ISet, System.Collections.ICollection,
System.Collections.IList, System.Collections.IDictionary,
System.Collections.Generic.ICollection<T>, System.Collections.Generic.IList<T>,
System.Collections.Generic.IDictionary<K, V>, Iesi.Collections.Generic.ISet<T> ou…
qualquer coisa que você gostar!

As coleções são declaradas pelos elementos <set>, <list>, <map>,<bag>, <array> e


<primitive-array>. O <map> é representativo:

<map
name="propertyName"
table="table_name"
schema="schema_name"
lazy="true|false"
inverse="true|false"
cascade="all|none|save-update|delete|all-delete-orphan"
sort="unsorted|natural|comparatorClass"
order-by="column_name asc|desc"
where="arbitrary sql where condition"
fetch="select|join"
batch-size="N" access="field|property|ClassName"
optimistic-lock="true|false"
generic="true|false"
>

<key .... />


<index .... />
<element .... />
</map>

11. Transações
Uma transação é uma unidade de execução indivisível (ou atômica). Isso
significa dizer que todas as etapas pertencentes a uma transação são
completamente finalizadas ou nenhuma delas termina.
Para exemplificar o conceito de transações, considere um exemplo clássico
de transferência entre contas bancárias: transferir R$ 150,00 da conta corrente do
cliente A para a conta corrente do cliente B. Basicamente, as operações que
compõem a transação são:

1º) Debitar R$ 150,00 da conta corrente do cliente A


2º) Creditar R$ 150,00 na conta corrente do cliente B
Para a efetivação da transação descrita acima, seria necessário seguir os
seguintes passos:
1º) Ler o saldo da conta corrente A (xA)

NHibernate – Persistência de Dados em Plataforma .NET 38


2º) Calcular o débito de R$ 150,00 da conta corrente A (dA = xA – 150,00)
3º) Gravar na base de dados o novo saldo da conta corrente A (xA = dA)
4º) Ler o saldo da conta corrente B (xB)
5º) Calcular o crédito de R$ 150,00 na conta corrente B (dB = xB + 150,00)
6º) Gravar na base de dados o novo saldo da conta corrente B (xB = dB)

Caso ocorra algum problema (por exemplo: falta de energia, falha no


computador, falha no programa, etc.), a execução dos passos anteriores pode ser
interrompida. Se, por exemplo, houvesse interrupção logo após a execução do 3º
passo, a conta A teria um débito de R$ 150,00 e ainda não teria sido creditado R$
150,00 na conta corrente B. Neste caso, o banco de dados estaria em um estado
inconsistente, afinal, R$ 150,00 teriam “sumido” da conta A sem destino. Dessa
maneira, é de suma importância garantir que esses seis passos sejam totalmente
executados. Caso haja alguma falha antes da conclusão do último passo, deve-se
garantir também que os passos já executados serão desfeitos, de forma que ou
todos os passos são executados ou todos os passos não são executados. Para
garantir a consistência do banco, esses seis passos devem ser executados dentro
de uma transação, já que ela é uma unidade de execução atômica.
Resumindo, uma transação garante que a seqüência de operações dentro da
mesma seja executada de forma única, ou seja, na ocorrência de erro em alguma
das operações dentro da transação todas as operações realizadas desde o início
podem ser revertidas e as alterações no banco de dados desfeitas, garantindo
assim, a unicidade do processo. A transação pode ter dois fins: commit ou
rollback.
Quando a transação sofre commit, todas as modificações nos dados
realizadas pelas operações presentes na transação são salvas. Quando a transação
sofre rollback, todas as modificações nos dados realizadas pelas operações
presentes na transação são desfeitas.
Para que um banco de dados garanta a integridade dos seus dados deve
possuir quatro características, conhecidas como ACID:

• Atomicidade: o banco de dados deve garantir que todas as transações


sejam indivisíveis.
• Consistência: após a execução de uma transação, o banco de dados deve
continuar consistente, ou seja, deve continuar com um estado válido.
• Isolamento: mesmo que várias transações ocorram paralelamente (ou
concorrentemente), nenhuma transação deve influenciar nas outras.
Resultados parciais de uma transação não devem ser “vistos” por outras
transações executadas concorrentemente.
• Durabilidade: após a finalização de uma transação, todas as alterações
feitas por ela no banco de dados devem ser duráveis, mesmo havendo
falhas no sistema após a sua finalização.

NHibernate – Persistência de Dados em Plataforma .NET 39


11.1 Modelos de Transações

As definições do início de uma transação, de seu fim e das ações que devem
ser tomadas na ocorrência de falhas são feitas através de um modelo de transação.
Existem diversos modelos encontrados na literatura. Nesta seção serão abordados
apenas quatro: Flat Transactions, Nested Transactions, Chained Transactions e Join
Transactions.
• Flat Transaction. Modelo mais utilizado pela maioria dos Sistemas
Gerenciadores de Banco de Dados (SGBD) e Gerenciadores de Transações.
Conhecida como modelo de transações planas por apresentar uma única
camada de controle, ou seja, todas as operações dentro da transação são
tratadas como uma única unidade de trabalho.
• Nested Transaction. Este modelo, também conhecido como Modelo de
Transações Aninhadas, possibilita que uma transação possa ser formada por
várias sub-transações. Em outras palavras, uma única transação pode ser
dividida em diversas unidades de trabalho, com cada unidade operando
independente uma das outras. A propriedade de atomicidade é válida para
as sub-transações. Além disso, uma transação não pode ser validada até
que todas as suas sub-transações tenham sido finalizadas. Se uma
transação for interrompida, todas as suas sub-transações também serão. O
contrário não é verdadeiro, já que se uma sub-transação for abortada a
transação que a engloba pode: ignorar o erro; desfazer a sub-transação;
iniciar uma outra sub-transação.
• Chained Transaction. Também conhecido como Modelo de Transações
Encadeadas, esse modelo tem como objetivo desfazer as operações de uma
transação em caso de erro com a menor perda de trabalho possível. Uma
transação encadeada consiste em um conjunto de sub-transações
executadas seqüencialmente, em que à medida que as sub-transações vão
sendo executadas, são validadas e não podem mais ser desfeitas. Os
resultados do conjunto de transações só serão visíveis ao final da execução
de todas elas.
• Join Transaction. Esse modelo permite que duas transações sejam unidas
em uma só, de forma que todos os recursos passam a ser compartilhados.

11.2 Transações e Banco de Dados


Uma transação de banco de dados é formada por um conjunto de operações
que manipulam os dados. A atomicidade de uma transação é garantida por duas
operações: commit e rollback.
Os limites das operações de uma transação devem ser demarcados. Assim,
é possível saber a partir de qual operação a transação é iniciada e em qual
operação ela finalizada. Ao final da execução da última operação que pertence à
transação, todas as alterações no banco de dados realizadas pelas operações que
compõe a transação devem ser confirmadas, ou seja, um commit é realizado. Se
houver algum erro durante a execução de algumas das suas operações, todas as
operações da transação que já foram executadas devem ser desfeitas, ou seja, um
rollback é realizado. A Figura abaixo ilustra esses conceitos.

NHibernate – Persistência de Dados em Plataforma .NET 40


Estados do sistema durante uma transação

12. Concorrência
Em algumas situações pode acontecer que duas ou mais transações que
ocorrem paralelamente leiam e atualizem o mesmo dado. Considerando que duas
transações leiam um mesmo dado x quase que simultaneamente. Ambas as
transações vão manipular esse mesmo dado com operações diferentes e atualizá-lo
na base de dados. Para exemplificar, a Listagem abaixo apresenta um exemplo de duas
transações concorrentes manipulando o mesmo dado x.

No primeiro passo, ambas as transações lêem o dado x com o mesmo valor


(2). Em seguida, T1 soma o valor x que leu com 1 e o valor de x para T1 passa a
ser 3 (2 + 1). Já T2, soma o valor de x lido a 3 e x passa a ter o valor 5 (2 + 3).
Por fim, ambos T1 e T2 gravarão os novos valores de x calculados na base de
dados, respectivamente. Como não há controle de concorrência de acesso ao dado
x, o seu valor final corresponderá a 5, ou seja, o valor calculado por T2,
significando que as alterações feitas por T1 foram descartadas.

Exemplo de Transações Concorrentes

NHibernate – Persistência de Dados em Plataforma .NET 41


Para evitar a situação descrita anteriormente, deve-se controlar o acesso
concorrente ao dado, ou seja, deve-se implementar o mecanismo de Locking. O
gerenciamento de locking e da concorrência pode ser feito de duas formas:

• Pessimista: utilizar o controle pessimista significa que se uma transação T1


lê um dado e tem a intenção de atualizá-lo, esse dado será bloqueado
(nenhuma outra transação poderá lê-lo) até que T1 o libere, normalmente
após a sua atualização.
• Otimista: utilizar o controle otimista significa que se T1 lê e altera um dado
ele não será bloqueado durante o intervalo entre a leitura e atualização.
Caso uma outra transação T2 tenha lido esse mesmo dado antes de T1 o
atualizá-lo tente alterá-lo em seguida na base de dados, um erro de violação
de concorrência deve ser gerado.

13. HQL – a linguagem de consulta hibernate


O Nhibernate é equipado com uma linguagem de consulta extremamente poderosa e que
(intencionalmente) parece muito como SQL. Mas Não seja enganado pela sintaxe: HQL é
totalmente orientada a objeto, e utiliza noções de herança, polimorfismo e associação

13.1 Case-Sensitive

Consultas são case-sensitive, exceto para nomes de classes e propriedades .NET.


Portanto, SeLeCT é o mesmo que SElect e o mesmo que selecT.

Este manual usa palavras-chave HQL em minúsculo. Alguns usuários fazem consultas com
palavras-chave maiúsculas, mas encontramos essa convenção quando incorporamos no
código Java.

13.2 A cláusura FROM

A consulta mais simples possível do NHibernate é:


from Eg.Cat

Esse script retorna todas as instâncias da classe Eg.Cat

Na maioria das vezes, você precisará atribuir um alias, pois você irá deseja se referir à Cat
em outras partes da consulta.

from Eg.Cat as cat

NHibernate – Persistência de Dados em Plataforma .NET 42


13.3 Associações e junções

Nós também podemos atribuir aliases para entidades associadas, ou mesmo a elementos de
uma coleção de valores, usando um join.
from Eg.Cat as cat
inner join cat.Mate as mate
left outer join cat.Kittens as kitten

from Eg.Cat as cat left join cat.Mate.Kittens as kittens

from Formula form full join form.Parameter param

O tipos de associações join são suportados pelo ANSI SQL

• inner join
• left outer join
• right outer join
• full join

13.4 A cláusura select

A clausura select retorna todos os objetos e propiedades que foram definidos na consulta.

select mate
from Eg.Cat as cat
inner join cat.Mate as mate

13.5 Funções Agregadas

Consultas HQL ainda podem retornar os resultados das funções agregadas nas
propriedades:

select avg(cat.Weight), sum(cat.Weight), max(cat.Weight), count(cat)


from Eg.Cat cat

Coleções também podem aparecer dentro funções agregadas na cláusula select.


select cat, count( elements(cat.Kittens) )
from Eg.Cat cat group by cat

NHibernate – Persistência de Dados em Plataforma .NET 43


As funções suportadas são:

avg(...), sum(...), min(...), max(...)

count(*)

count(...), count(distinct ...), count(all...)

O distinct e all também podem ser usados e tem a mesma semântica do SQL.

select distinct cat.Name from Eg.Cat cat

select count(distinct cat.Name), count(cat) from Eg.Cat cat

13.6 Consultas Polimórficas

Uma consulta como:

from Eg.Cat as cat

Retorna as instâncias não só do Cat, mas também da subclasses DomesticCat. Talvez as


consultas Consultas NHibernate podem nomear algumas classes e interfaces .NET na
cláusula FROM. A consulta irá retornar instâncias de todas as classes persistentes que
estendem essa classe ou implementam a interface. A seguinte consulta retornaria todos os
objetos persistentes:

from System.Object o

A interface IName pode ser implentada por varias classes persistentes.

from Eg.Named n, Eg.Named m where n.Name = m.Name

13.7 Cláusura WHERE

A cláusula where lhe permite estreitar a lista de instâncias retornadas.

from Eg.Cat as cat where cat.Name='Fritz'

retorna instâncias do Cat chamado "Fritz".

NHibernate – Persistência de Dados em Plataforma .NET 44


select foo
from Eg.Foo foo, Eg.Bar bar
where foo.StartDate = bar.Date

irá retornar todas as instâncias do Foo para a qual existe um instância de Bar com uma
propriedade data igual à propriedade StartDate do Foo. Composto de expressões podem
tornar a cláusula WHERE extremamente poderosa. Considere:

from Eg.Cat cat where cat.Mate.Name is not null

Esta pergunta traduz em uma consulta SQL com uma tabela (inner) aderirida. Se você for
escrever algo como

from Eg.Foo foo


where foo.Bar.Baz.Customer.Address.City is not null

você iria terminar com uma consulta que exigiria quatro tabelas agrupadas no SQL.

O operador “= “ pode ser usado para comparar não apenas propriedades, mas também
casos:

from Eg.Cat cat, Eg.Cat rival where cat.Mate = rival.Mate

select cat, mate


from Eg.Cat cat, Eg.Cat mate
where cat.Mate = mate

A propriedade especial (lowercase) id pode ser utilizada para referenciar um identificador


a um único objeto. (Você também pode utilizar a propriedade name.)

from Eg.Cat as cat where cat.id = 123

from Eg.Cat as cat where cat.Mate.id = 69


A segunda consulta é eficiente. Não é necessário aderir uma tabela!

13.8 Expressões

Em HQL, como SQL, há o suporte a operadores matemáticos (+, -, * e /),


operadores de comparação (<, >, <>, <=, >=, =, between, not between, in e
not in), operadores lógicos (or, and e parênteses para agrupar expressões), o
operador LIKE e o símbolo ‘%’ também podem ser utilizados para pesquisas em
String, além de poder ordenar sua pesquisa em ordem descendente ou
ascendente.

NHibernate – Persistência de Dados em Plataforma .NET 45


13.9 A cláusura Group By
A consulta que retorna valores agregados podem ser agrupados por propriedades de uma
classe ou componentes.
select cat.Color, sum(cat.Weight), count(cat)
from Eg.Cat cat
group by cat.Color

select foo.id, avg( elements(foo.Names) ), max( indices(foo.Names) )


from Eg.Foo foo
group by foo.id
Nota: Você pode usar os contrutores elements e índices dentro da cláusura select, mesmo
me bancos de dados sem subselects.

13.10 A cláusura Order By


A lista retornada por uma consulta pode ser ordenada por qualquer propriedade de uma
classe ou componentes

from Eg.DomesticCat cat


order by cat.Name asc, cat.Weight desc, cat.Birthdate

Os indicadores ASC e DESC são opcionais

13.11 Subqueries

Para bancos de dados que oferecem suporte ao subselects, o NHibernate suporta


subconsultas em consultas. Uma subconsulta deve estar entre parênteses (geralmente por
uma chamada de função agregada SQL), mesmo correlacionadas subconsultas
(subconsultas que se referem a um alias na consulta externa).

from Eg.Cat as fatcat


where fatcat.Weight > (
select avg(cat.Weight) from Eg.DomesticCat cat
)

from Eg.DomesticCat as cat


where cat.Name = some (
select name.NickName from Eg.Name as name
)

from Eg.Cat as cat


where not exists (
from eg.Cat as mate where mate.Mate = cat
)

from Eg.DomesticCat as cat

NHibernate – Persistência de Dados em Plataforma .NET 46


where cat.Name not in (
select name.NickName from Eg.Name as name
)

14. Criteria
A API Query By Criteria (QBC) permite realizar consultas por manipulação de
critérios em tempo de execução. Esta abordagem permite definir restrições
dinamicamente sem que haja manipulação de Strings, a sua flexibilidade e poder
é bem menor do que HQL e não suporta agregações nem projeções.

14.1 Criando uma instância ICriteria

A interface NHibernate.ICriteria representa uma consulta particular contra uma classe


persistente. O ISession é uma fábrica para ICriteria instâncias.

ICriteria crit = sess.CreateCriteria (typeof (Cat)); crit.SetMaxResults (50); Lista Cats


crit.List = ();

14.1.1 Criteria com restrição


IList cats = sess.CreateCriteria(typeof(Cat))
.Add( Expression.Like("Name", "Fritz%") )
.Add( Expression.Between("Weight", minWeight, maxWeight) )
.List();

14.1.2 Criteria com restrições Like

// Create a string parameter for the SqlString below


Parameter paramName = new Parameter("someName", new StringSqlType());

IList cats = sess.CreateCriteria(typeof(Cat))


.Add( Expression.Sql(
new SqlString( new object[] {
"lower({alias}.Name) like lower(",
paramName,
")" } ),
"Fritz%",
NHibernateUtil.String ).List();

NHibernate – Persistência de Dados em Plataforma .NET 47


14.2 Ordenando o resultado

Você pode ordenar o resultado usando o Hibernate.Expression.Order.

IList cats = sess.CreateCriteria(typeof(Cat))


.Add( Expression.Like("Name", "F%")
.AddOrder( Order.Asc("Name") )
.AddOrder( Order.Desc("Age") )
.SetMaxResults(50)
.List();

14.3 Associações
Faça associações usando CreateCriteria().
IList cats = sess.CreateCriteria(typeof(Cat))
.Add( Expression.Like("Name", "F%")
.CreateCriteria("Kittens")
.Add( Expression.Like("Name", "F%") )
.List();

15. Native SQL Queries


Você também pode expressar consultas no dialeto SQL nativo de seu banco de dados. Isso
é útil se você deseja utilizar recursos específicos do banco de dados, como a palavra-chave
CONNECT no Oracle. Isso também permite um caminho de migração de limpeza a partir
de um aplicativo com base em SQL/ADO.NET direto para NHibernate.

15.1 Criando uma consulta(IQuery) baseada em SQL


IQuery sqlQuery = sess.CreateSQLQuery("select {cat.*} from cats {cat}", "cat",
typeof(Cat));
sqlQuery.SetMaxResults(50);
IList cats = sqlQuery.List();

15.2 Referências de alias e propriedade

string sql = "select cat.originalId as {cat.Id}, "


+ " cat.mateid as {cat.Mate}, cat.sex as {cat.Sex}, "
+ " cat.weight*10 as {cat.Weight}, cat.name as {cat.Name}"
+ " from cat_log cat where {cat.Mate} = :catId"
IList loggedCats = sess.CreateSQLQuery(sql, "cat", typeof(Cat))
.SetInt64("catId", catId)
.List();

16. Consultas nomeadas SQL

NHibernate – Persistência de Dados em Plataforma .NET 48


Consultas Nomeadas SQL podem ser definidas no documento de mapeamento e chamadas
exatamente da mesma maneira como uma consulta HQL nomeada.

IList people = sess.GetNamedQuery("mySqlQuery")


.SetMaxResults(50)
.List();

<sql-query name="mySqlQuery">
<return alias="person" class="Eg.Person, Eg"/>
SELECT {person}.NAME AS {person.Name},
{person}.AGE AS {person.Age},
{person}.SEX AS {person.Sex}
FROM PERSON {person} WHERE {person}.NAME LIKE 'Hiber%'
</sql-query>

NHibernate – Persistência de Dados em Plataforma .NET 49


Referências:

http://www.hibernate.org/hib_docs/nhibernate/1.2/reference/en/html/

www.hibernate.org

ftp://raphaela:web@users.dca.ufrn.br/UnP2007/Hibernate_Anotacoes.pdf

Guia Inicial do Nhibernate: www.linhadecodigo.com.br/Artigo.aspx?id=546

NHibernate – Persistência de Dados em Plataforma .NET 50

Você também pode gostar