Hibernate Reference
Hibernate Reference
Documentação de
Referência Hibernate
3.6.0.CR2
iii
HIBERNATE - Persistência Rela...
iv
6.4. Custom types .................................................................................................. 154
6.4.1. Custom types using org.hibernate.type.Type .......................................... 154
6.4.2. Custom types using org.hibernate.usertype.UserType ............................. 156
6.4.3. Custom types using org.hibernate.usertype.CompositeUserType ............. 157
6.5. Type registry ................................................................................................... 158
7. Mapeamento de coleção .......................................................................................... 161
7.1. Coleções persistentes ..................................................................................... 161
7.2. How to map collections ................................................................................... 162
7.2.1. Chaves Externas de Coleção ................................................................ 166
7.2.2. Coleções indexadas ............................................................................. 166
7.2.3. Collections of basic types and embeddable objects ................................. 172
7.3. Mapeamentos de coleção avançados. .............................................................. 174
7.3.1. Coleções escolhidas ............................................................................. 174
7.3.2. Associações Bidirecionais ..................................................................... 176
7.3.3. Associações bidirecionais com coleções indexadas ................................ 180
7.3.4. Associações Ternárias .......................................................................... 182
7.3.5. Using an <idbag> ................................................................................. 182
7.4. Exemplos de coleções .................................................................................... 183
8. Mapeamento de associações ................................................................................... 189
8.1. Introdução ...................................................................................................... 189
8.2. Associações Unidirecionais .............................................................................. 189
8.2.1. Muitos-para-um .................................................................................... 189
8.2.2. Um-para-um ......................................................................................... 190
8.2.3. Um-para-muitos .................................................................................... 191
8.3. Associações Unidirecionais com tabelas associativas ........................................ 191
8.3.1. Um-para-muitos .................................................................................... 191
8.3.2. Muitos-para-um .................................................................................... 192
8.3.3. Um-para-um ......................................................................................... 193
8.3.4. Muitos-para-muitos ............................................................................... 193
8.4. Associações Bidirecionais ................................................................................ 194
8.4.1. Um-para-muitos/muitos-para-um ............................................................ 194
8.4.2. Um-para-um ......................................................................................... 195
8.5. Associações Bidirecionais com tabelas associativas .......................................... 196
8.5.1. Um-para-muitos/muitos-para-um ............................................................ 196
8.5.2. Um para um ........................................................................................ 197
8.5.3. Muitos-para-muitos ............................................................................... 198
8.6. Mapeamento de associações mais complexas .................................................. 199
9. Mapeamento de Componentes ................................................................................ 201
9.1. Objetos dependentes ...................................................................................... 201
9.2. Coleções de objetos dependentes ................................................................... 203
9.3. Componentes como índices de Map ................................................................ 204
9.4. Componentes como identificadores compostos ................................................. 205
9.5. Componentes Dinâmicos ................................................................................. 207
10. Mapeamento de Herança ....................................................................................... 209
v
HIBERNATE - Persistência Rela...
vi
13.2.2. Usando JTA ....................................................................................... 253
13.2.3. Tratamento de Exceção ...................................................................... 255
13.2.4. Tempo de espera de Transação .......................................................... 256
13.3. Controle de concorrência otimista .................................................................. 257
13.3.1. Checagem de versão da aplicação ...................................................... 257
13.3.2. Sessão estendida e versionamento automático .................................... 258
13.3.3. Objetos destacados e versionamento automático .................................. 259
13.3.4. Versionamento automático customizado ............................................... 259
13.4. Bloqueio Pessimista ...................................................................................... 260
13.5. Modos para liberar a conexão ........................................................................ 261
14. Interceptadores e Eventos ..................................................................................... 263
14.1. Interceptadores ............................................................................................. 263
14.2. Sistema de Eventos ...................................................................................... 265
14.3. Segurança declarativa do Hibernate ............................................................... 266
15. Batch processing ................................................................................................... 269
15.1. Inserção em lotes .......................................................................................... 269
15.2. Atualização em lotes ..................................................................................... 270
15.3. A interface de Sessão sem Estado ................................................................ 270
15.4. Operações no estilo DML .............................................................................. 271
16. HQL: A Linguagem de Consultas do Hibernate ..................................................... 275
16.1. Diferenciação de maiúscula e minúscula ........................................................ 275
16.2. A cláusula from ............................................................................................. 275
16.3. Associações e uniões .................................................................................... 276
16.4. Formas de sintáxe de uniões ......................................................................... 278
16.5. Referência à propriedade do identificador ....................................................... 278
16.6. A cláusula select ........................................................................................... 279
16.7. Funções de agregação .................................................................................. 280
16.8. Pesquisas Polimórficas .................................................................................. 281
16.9. A cláusula where .......................................................................................... 281
16.10. Expressões ................................................................................................. 283
16.11. A cláusula ordenar por ................................................................................ 287
16.12. A cláusula agrupar por ................................................................................ 288
16.13. Subconsultas ............................................................................................... 288
16.14. Exemplos de HQL ....................................................................................... 289
16.15. Atualização e correção em lote .................................................................... 292
16.16. Dicas & Truques ......................................................................................... 292
16.17. Componentes .............................................................................................. 293
16.18. Sintáxe do construtor de valores de linha ...................................................... 294
17. Consultas por critérios .......................................................................................... 295
17.1. Criando uma instância Criteria ....................................................................... 295
17.2. Limitando o conjunto de resultados ................................................................ 295
17.3. Ordenando resultados ................................................................................... 296
17.4. Associações .................................................................................................. 297
17.5. Busca de associação dinâmica ...................................................................... 298
vii
HIBERNATE - Persistência Rela...
viii
21.2.6. Compatibilidade de Estratégia de Concorrência de Cache Provedor ....... 345
21.3. Gerenciando os caches ................................................................................. 346
21.4. O Cache de Consulta .................................................................................... 347
21.4.1. Ativação do cache de consulta ............................................................ 347
21.4.2. Regiões de cache de consulta ............................................................ 349
21.5. Entendendo o desempenho da Coleção ......................................................... 349
21.5.1. Taxonomia ......................................................................................... 349
21.5.2. Listas, mapas, bags de id e conjuntos são coleções mais eficientes para
atualizar ........................................................................................................ 350
21.5.3. As Bags e listas são as coleções de inversão mais eficientes. ............... 351
21.5.4. Deletar uma vez ................................................................................. 351
21.6. Monitorando desempenho .............................................................................. 352
21.6.1. Monitorando uma SessionFactory ........................................................ 352
21.6.2. Métricas ............................................................................................. 353
22. Guia de Toolset ...................................................................................................... 355
22.1. Geração de esquema automático ................................................................... 355
22.1.1. Padronizando o esquema ................................................................... 356
22.1.2. Executando a ferramenta .................................................................... 359
22.1.3. Propriedades ...................................................................................... 359
22.1.4. Usando o Ant ..................................................................................... 360
22.1.5. Atualizações de esquema incremental ................................................. 360
22.1.6. Utilizando Ant para atualizações de esquema incremental ..................... 361
22.1.7. Validação de esquema ....................................................................... 361
22.1.8. Utilizando Ant para validação de esquema ........................................... 362
23. Additional modules ................................................................................................ 363
23.1. Bean Validation ............................................................................................. 363
23.1.1. Adding Bean Validation ....................................................................... 363
23.1.2. Configuration ...................................................................................... 363
23.1.3. Catching violations .............................................................................. 365
23.1.4. Database schema ............................................................................... 365
23.2. Hibernate Search .......................................................................................... 366
23.2.1. Description ......................................................................................... 366
23.2.2. Integration with Hibernate Annotations ................................................. 366
24. Exemplo: Pai/Filho ................................................................................................. 367
24.1. Uma nota sobre as coleções ......................................................................... 367
24.2. Bidirecional um-para-muitos ........................................................................... 367
24.3. Ciclo de vida em Cascata .............................................................................. 369
24.4. Cascatas e unsaved-value ............................................................................. 371
24.5. Conclusão ..................................................................................................... 371
25. Exemplo: Aplicativo Weblog .................................................................................. 373
25.1. Classes Persistentes ..................................................................................... 373
25.2. Mapeamentos Hibernate ................................................................................ 374
25.3. Código Hibernate .......................................................................................... 376
26. Exemplo: Vários Mapeamentos .............................................................................. 381
ix
HIBERNATE - Persistência Rela...
x
Prefácio
Working with both Object-Oriented software and Relational Databases can be cumbersome
and time consuming. Development costs are significantly higher due to a paradigm mismatch
between how data is represented in objects versus relational databases. Hibernate is an Object/
Relational Mapping solution for Java environments. The term Object/Relational Mapping refers
to the technique of mapping data from an object model representation to a relational data model
representation (and visa versa). See http://en.wikipedia.org/wiki/Object-relational_mapping for a
good high-level discussion.
Nota
While having a strong background in SQL is not required to use Hibernate, having
a basic understanding of the concepts can greatly help you understand Hibernate
more fully and quickly. Probably the single best background is an understanding of
data modeling principles. You might want to consider these resources as a good
starting point:
• http://www.agiledata.org/essays/dataModeling101.html
• http://en.wikipedia.org/wiki/Data_modeling
Hibernate not only takes care of the mapping from Java classes to database tables (and from
Java data types to SQL data types), but also provides data query and retrieval facilities. It can
significantly reduce development time otherwise spent with manual data handling in SQL and
JDBC. Hibernate’s design goal is to relieve the developer from 95% of common data persistence-
related programming tasks by eliminating the need for manual, hand-crafted data processing
using SQL and JDBC. However, unlike many other persistence solutions, Hibernate does not hide
the power of SQL from you and guarantees that your investment in relational technology and
knowledge is as valid as always.
Hibernate may not be the best solution for data-centric applications that only use stored-
procedures to implement the business logic in the database, it is most useful with object-
oriented domain models and business logic in the Java-based middle-tier. However, Hibernate
can certainly help you to remove or encapsulate vendor-specific SQL code and will help with the
common task of result set translation from a tabular representation to a graph of objects.
Por favor siga os seguintes passos, caso você seja inexperiente com o Hibernate, Mapeamento
Objeto/Relacional ou mesmo Java:
1. Read Capítulo 1, Tutorial for a tutorial with step-by-step instructions. The source code for the
tutorial is included in the distribution in the doc/reference/tutorial/ directory.
2. Read Capítulo 2, Arquitetura to understand the environments where Hibernate can be used.
xi
Prefácio
4. Use this reference documentation as your primary source of information. Consider reading
[JPwH] if you need more help with application design, or if you prefer a step-by-step tutorial. Also
visit http://caveatemptor.hibernate.org and download the example application from [JPwH].
5. As respostas das perguntas mais freqüentes podem ser encontradas no website Hibernate.
There are a number of ways to become involved in the Hibernate community, including
• Trying your hand at fixing some bugs or implementing enhancements. Again, see http://
hibernate.org/issuetracker.html details.
• There are forums for users to ask questions and receive help from the community.
• Helping improve or translate this documentation. Contact us on the developer mailing list if you
have interest.
xii
Tutorial
Intencionado para novos usuários, este capítulo fornece uma introdução detalhada do Hibernate,
começando com um aplicativo simples usando um banco de dados em memória. O tutorial é
baseado num tutorial anterior desenvolvido por Michael Gloegl. Todo o código está contido no
diretório tutorials/web da fonte do projeto.
Importante
Este tutorial espera que o usuário tenha conhecimento de ambos Java e SQL.
Caso você tenha um conhecimento limitado do JAVA ou SQL, é recomendado que
você inicie com uma boa introdução àquela tecnologia, antes de tentar entender
o Hibernate.
Nota
Nota
Mesmo que usando qualquer banco de dados do qual você se sinta confortável,
nós usaremos HSQLDB [http://hsqldb.org/] (o em memória, banco de dados Java)
para evitar a descrição de instalação/configuração de quaisquer servidores do
banco de dados.
1.1.1. Configuração
1
Capítulo 1. Tutorial
Nós usaremos Maven neste tutorial, tirando vantagem destas capacidades de dependência
transitiva assim como a habilidade de muitos IDEs de configurar automaticamente um projeto
baseado no descritor maven.
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/
maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.hibernate.tutorials</groupId>
<artifactId>hibernate-tutorial</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>First Hibernate Tutorial</name>
<build>
<!-- we dont want the version to be part of the generated war file name -->
<finalName>${artifactId}</finalName>
</build>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
</dependency>
<!-- Because this is a web app, we also have a dependency on the servlet api. -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</dependency>
<!-- Hibernate uses slf4j for logging, for our purposes here use the simple backend -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</dependency>
<!-- Hibernate gives you a choice of bytecode providers between cglib and javassist -->
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
</dependency>
</dependencies>
</project>
Dica
It is not a requirement to use Maven. If you wish to use something else to build
this tutorial (such as Ant), the layout will remain the same. The only change is
that you will need to manually account for all the needed dependencies. If you
2
A primeira Classe
package org.hibernate.tutorial.domain;
import java.util.Date;
public Event() {}
3
Capítulo 1. Tutorial
Você pode ver que esta classe usa o padrão JavaBean para o nome convencional dos métodos
de propriedade getter e setter, como também a visibilidade privada dos campos. Este é um
padrão de projeto recomendado, mas não requerido. O Hibernate pode também acessar campos
diretamente, o benefício para os métodos de acesso é a robustez para o refactoring.
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.hibernate.tutorial.domain">
[...]
</hibernate-mapping
>
Note que o Hibernate DTD é muito sofisticado. Você pode usar isso para auto-conclusão no
mapeamento XML dos elementos e funções no seu editor ou IDE. Você também pode abrir
o arquivo DTD no seu editor. Esta é a maneira mais fácil de ter uma visão geral de todos os
elementos e funções e dos padrões, como também alguns comentários. Note que o Hibernate
não irá carregar o arquivo DTD da web, e sim da classpath da aplicação. O arquivo DTD está
4
O mapeamento do arquivo
Importante
Nós omitiremos a declaração do DTD nos exemplos futuros para encurtar o código.
Isto, é claro, não é opcional.
<hibernate-mapping package="org.hibernate.tutorial.domain">
</class>
</hibernate-mapping>
Até agora, informamos o Hibernate sobre como fazer para persistir e carregar objetos da
classe Event da tabela EVENTS, cada instância representada por uma coluna na tabela. Agora,
continuaremos com o mapeamento de uma única propriedade identificadora para as chaves
primárias da tabela. Além disso, como não precisamos nos preocupar em manipular este
identificador, iremos configurar uma estratégia de geração de id’s do Hibernate para uma coluna
de chave primária substituta:
<hibernate-mapping package="org.hibernate.tutorial.domain">
</hibernate-mapping>
5
Capítulo 1. Tutorial
Dica
native is no longer consider the best strategy in terms of portability. for further
discussion, see Seção 28.4, “Geração do identificador”
<hibernate-mapping package="org.hibernate.tutorial.domain">
</hibernate-mapping>
Assim como com o elemento id, a função name do elemento property informa ao Hibernate qual
método getter e setter deverá usar. Assim, neste caso, o Hibernate irá procurar pelos métodos
getDate(), setDate(), getTitle() e setTitle().
Nota
Porque fazer o mapeamento da propriedade date incluído na função column, e no
title não fazer? Sem a função column o Hibernate, por padrão, utiliza o nome da
propriedade como o nome da coluna. Isto funciona bem para o title. Entretanto,
o date é uma palavra-chave reservada na maioria dos bancos de dados, por isso
seria melhor mapeá-lo com um nome diferente.
O mapeamento do title também não possui a função type. O tipo que declaramos e utilizamos
nos arquivos mapeados, não são como você esperava, ou seja, funções de dados Java. Eles
também não são como os tipos de base de dados SQL. Esses tipos podem ser chamados de
Tipos de mapeamento Hibernate, que são conversores que podem traduzir tipos de dados do
Java para os tipos de dados SQL e vice-versa. Novamente, o Hibernate irá tentar determinar
a conversão correta e mapeará o type próprio, caso o tipo da função não estiver presente no
mapeamento. Em alguns casos, esta detecção automática (que usa Reflection sobre as classes
6
Configuração do Hibernate
Java) poderá não ter o padrão que você espera ou necessita. Este é o caso com a propriedade
date. O Hibernate não sabe se a propriedade, que é do java.util.Date, pode mapear para
uma coluna do tipo date do SQL, timestamp ou time. Nós preservamos as informações sobre
datas e horas pelo mapeamento da propriedade com um conversor timestamp.
Dica
Nestas alturas, você deve possuir a classe persistente e seu arquivo de mapeamento prontos.
É o momento de configurar o Hibernate. Primeiro, vamos configurar o HSQLDB para rodar no
"modo do servidor".
Nota
Nós realizamos isto para que aqueles dados permaneçam entre as execuções.
Nós utilizaremos o Maven exec plugin para lançar o servidor HSQLDB pela execução:
mvn exec:java -Dexec.mainClass="org.hsqldb.Server" -Dexec.args="-database.0
file:target/data/tutorial". Você pode ver ele iniciando e vinculando ao soquete TCP/IP,
aqui será onde nossa aplicação irá se conectar depois. Se você deseja iniciar uma nova base
de dados durante este tutorial, finalize o HSQLDB, delete todos os arquivos no diretório target/
data, e inicie o HSQLBD novamente.
O Hibernate conectará ao banco de dados no lugar de sua aplicação, portanto ele precisará saber
como obter as conexões. Para este tutorial nós usaremos um pool de conexão autônomo (ao
invés de javax.sql.DataSource). O Hibernate vem com o suporte para dois terços dos pools
de conexão JDBC de código aberto: c3p0 [https://sourceforge.net/projects/c3p0] e proxool [http://
proxool.sourceforge.net/]. No entanto, nós usaremos o pool de conexão interna do Hibernate para
este tutorial.
7
Capítulo 1. Tutorial
Cuidado
<hibernate-configuration>
<session-factory>
8
Construindo com o Maven
<mapping resource="org/hibernate/tutorial/domain/Event.hbm.xml"/>
</session-factory>
</hibernate-configuration
>
Nota
Perceba que este arquivo de configuração especifica um DTD diferente
Dica
In most cases, Hibernate is able to properly determine which dialect to use. See
Seção 28.3, “Resolução do Dialeto” for more information.
9
Capítulo 1. Tutorial
[INFO] ------------------------------------------------------------------------
[INFO] Building First Hibernate Tutorial
[INFO] task-segment: [compile]
[INFO] ------------------------------------------------------------------------
[INFO] [resources:resources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [compiler:compile]
[INFO] Compiling 1 source file to /home/steve/projects/sandbox/hibernateTutorial/target/classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2 seconds
[INFO] Finished at: Tue Jun 09 12:25:25 CDT 2009
[INFO] Final Memory: 5M/547M
[INFO] ------------------------------------------------------------------------
Criaremos uma classe de ajuda HibernateUtil, que cuida da inicialização e faz acesso a uma
org.hibernate.SessionFactory mais conveniente.
package org.hibernate.tutorial.util;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
10
Carregando e salvando objetos
Você precisará agora configurar um sistema de logging. O Hibernate usa logging comuns e lhe
oferece a escolha entre o Log4j e o logging do JDK 1.4 . A maioria dos desenvolvedores prefere o
Log4j: copie log4j.properties da distribuição do Hibernate no diretório etc/, para seu diretório
src, depois vá em hibernate.cfg.xml. Dê uma olhada no exemplo de configuração e mude as
configurações se você quiser ter uma saída mais detalhada. Por padrão, apenas as mensagens
de inicialização do Hibernate são mostradas no stdout.
O tutorial de infra-estrutura está completo e nós já estamos preparados para algum trabalho de
verdade com o Hibernate.
package org.hibernate.tutorial;
import org.hibernate.Session;
import java.util.*;
import org.hibernate.tutorial.domain.Event;
import org.hibernate.tutorial.util.HibernateUtil;
if (args[0].equals("store")) {
mgr.createAndStoreEvent("My Event", new Date());
}
HibernateUtil.getSessionFactory().close();
11
Capítulo 1. Tutorial
session.getTransaction().commit();
}
Importante
12
Carregando e salvando objetos
uma Session para cada operação. Isto é pura coincidência, o exemplo simplesmente não
é complexo o bastante para mostrar qualquer outra abordagem. O escopo de um Hibernate
org.hibernate.Session é flexível, mas você nunca deve configurar seu aplicativo para utilizar
um novo Hibernate org.hibernate.Session para aoperação de banco de dados every. Portanto,
mesmo que você o veja algumas vezes mais nos seguintes exemplos, considere session-per-
operation como um anti-modelo. Um aplicativo da web real será demonstrado mais adiante neste
tutorial.
See Capítulo 13, Transações e Concorrência for more information about transaction handling
and demarcation. The previous example also skipped any error handling and rollback.
Para rodar isto, nós faremos uso do Maven exec plugin para chamar
nossa classe com a instalação do classpath necessária: mvn exec:java -
Dexec.mainClass="org.hibernate.tutorial.EventManager" -Dexec.args="store"
Nota
[java] Hibernate: insert into EVENTS (EVENT_DATE, title, EVENT_ID) values (?, ?, ?)
Adicionamos uma opção para o método principal com o objetivo de listar os eventos arquivados:
if (args[0].equals("store")) {
mgr.createAndStoreEvent("My Event", new Date());
}
else if (args[0].equals("list")) {
List events = mgr.listEvents();
for (int i = 0; i < events.size(); i++) {
Event theEvent = (Event) events.get(i);
System.out.println(
"Event: " + theEvent.getTitle() + " Time: " + theEvent.getDate()
);
}
}
13
Capítulo 1. Tutorial
session.beginTransaction();
List result = session.createQuery("from Event").list();
session.getTransaction().commit();
return result;
}
Here, we are using a Hibernate Query Language (HQL) query to load all existing Event objects
from the database. Hibernate will generate the appropriate SQL, send it to the database and
populate Event objects with the data. You can create more complex queries with HQL. See
Capítulo 16, HQL: A Linguagem de Consultas do Hibernate for more information.
Agora podemos chamar nossa nova funcionalidade usando, novamente, o Maven exec
plugin: mvn exec:java -Dexec.mainClass="org.hibernate.tutorial.EventManager" -
Dexec.args="list"
package org.hibernate.tutorial.domain;
public Person() {}
<hibernate-mapping package="org.hibernate.tutorial.domain">
14
Uma associação unidirecional baseada em Configuração
</hibernate-mapping>
<mapping resource="events/Event.hbm.xml"/>
<mapping resource="events/Person.hbm.xml"/>
Crie agora uma associação entre estas duas entidades. As pessoas (Person) podem participar
de eventos, e eventos possuem participantes. As questões de design com que teremos de lidar
são: direcionalidade, multiplicidade e comportamento de coleção.
Antes de mapearmos esta associação, pense no outro lado. Claramente, poderíamos apenas
fazer isto de forma unidirecional. Ou poderíamos criar outra coleção no Event, se quisermos
navegar de ambas direções. Isto não é necessário, de uma perspectiva funcional. Você poderá
sempre executar uma consulta explícita para recuperar os participantes de um evento em
particular. Esta é uma escolha de design que cabe a você, mas o que é claro nessa discussão é
a multiplicidade da associação: "muitos" válidos em ambos os lados, nós chamamos isto de uma
15
Capítulo 1. Tutorial
</class>
O Hibernate suporta todo tipo de mapeamento de coleção, sendo um set mais comum. Para uma
associação muitos-para-muitos ou relacionamento de entidade n:m, é necessária uma tabela de
associação. Cada linha nessa tabela representa um link entre uma pessoa e um evento. O nome
da tabela é configurado com a função table do elemento set. O nome da coluna identificadora
na associação, pelo lado da pessoa, é definido com o elemento key, o nome da coluna pelo
lado dos eventos, é definido com a função column do many-to-many. Você também precisa dizer
para o Hibernate a classe dos objetos na sua coleção (a classe do outro lado das coleções de
referência).
_____________ __________________
| | | | _____________
| EVENTS | | PERSON_EVENT | | |
|_____________| |__________________| | PERSON |
| | | | |_____________|
| *EVENT_ID | <--> | *EVENT_ID | | |
| EVENT_DATE | | *PERSON_ID | <--> | *PERSON_ID |
| TITLE | |__________________| | AGE |
|_____________| | FIRSTNAME |
| LASTNAME |
|_____________|
16
Trabalhando a associação
session.beginTransaction();
session.getTransaction().commit();
}
Você pode também querer carregar pessoas e eventos em diferentes unidades de trabalho. Ou
você modifica um objeto fora de um org.hibernate.Session, quando não se encontra no estado
persistente (se já esteve neste estado anteriormente, chamamos esse estado de detached). Você
pode até mesmo modificar uma coleção quando esta se encontrar no estado detached:
session.getTransaction().commit();
session2.getTransaction().commit();
}
17
Capítulo 1. Tutorial
A chamada update cria um objeto persistente novamente, pode-se dizer que ele liga o objeto a
uma nova unidade de trabalho, assim qualquer modificação que você faça neste objeto enquanto
estiver no estado desanexado pode ser salvo no banco de dados. Isso inclui qualquer modificação
(adição/exclusão) que você faça em uma coleção da entidade deste objeto.
Bem, isso não é de grande utilidade na nossa situação atual, porém, é um importante conceito
que você pode criar em seu próprio aplicativo. No momento, complete este exercício adicionando
uma ação ao método principal da classe EventManager e chame-o pela linha de comando.
Se você precisar dos identificadores de uma pessoa ou evento - o método save() retornará
estes identificadores (você poderá modificar alguns dos métodos anteriores para retornar aquele
identificador):
else if (args[0].equals("addpersontoevent")) {
Long eventId = mgr.createAndStoreEvent("My Event", new Date());
Long personId = mgr.createAndStorePerson("Foo", "Bar");
mgr.addPersonToEvent(personId, eventId);
System.out.println("Added person " + personId + " to event " + eventId);
}
Este foi um exemplo de uma associação entre duas classes igualmente importantes: duas
entidades. Como mencionado anteriormente, há outras classes e tipos dentro de um modelo
típico, geralmente "menos importante". Alguns você já viu, como um int ou uma String.
Nós chamamos essas classes de tipos de valores, e suas instâncias dependem de uma
entidade particular. As instâncias desses tipos não possuem sua própria identidade, nem são
compartilhados entre entidades. Duas pessoas não referenciam o mesmo objeto firstname
mesmo se elas tiverem o mesmo objeto firstname. Naturalmente, os tipos de valores não são
apenas encontrados dentro da JDK, mas você pode também criar suas classes como, por
exemplo, Address ou MonetaryAmount. De fato, no aplicativo Hibernate todas as classes JDK
são consideradas tipos de valores.
Você também pode criar uma coleção de tipo de valores. Isso é conceitualmente muito diferente
de uma coleção de referências para outras entidades, mas em Java parece ser quase a mesma
coisa.
18
Coleção de valores
A diferença comparada com o mapeamento anterior se encontra na parte element, que informa
ao Hibernate que a coleção não contém referências à outra entidade, mas uma coleção de
elementos do tipo String. O nome da tag em minúsculo indica que se trata de um tipo/conversor
de mapeamento do Hibernate. Mais uma vez, a função table do elemento set determina o nome
da tabela para a coleção. O elemento key define o nome da coluna de chave estrangeira na
tabela de coleção. A função column dentro do elemento element define o nome da coluna onde
os valores da String serão armazenados.
_____________ __________________
| | | | _____________
| EVENTS | | PERSON_EVENT | | | ___________________
|_____________| |__________________| | PERSON | | |
| | | | |_____________| | PERSON_EMAIL_ADDR |
| *EVENT_ID | <--> | *EVENT_ID | | | |___________________|
| EVENT_DATE | | *PERSON_ID | <--> | *PERSON_ID | <--> | *PERSON_ID |
| TITLE | |__________________| | AGE | | *EMAIL_ADDR |
|_____________| | FIRSTNAME | |___________________|
| LASTNAME |
|_____________|
Você pode observar que a chave primária da tabela da coleção é na verdade uma chave
composta, usando as duas colunas. Isso também implica que cada pessoa não pode ter
endereços de e-mail duplicados, o que é exatamente a semântica que precisamos para um set
em Java.
Você pode agora tentar adicionar elementos à essa coleção, do mesmo modo que fizemos
anteriormente ligando pessoas e eventos. É o mesmo código em Java:
19
Capítulo 1. Tutorial
session.getTransaction().commit();
}
Desta vez não utilizamos uma consulta fetch (busca) para inicializar a coleção. Monitore o log
SQL e tente otimizá-lo com árdua busca.
Agora iremos mapear uma associação bidirecional. Você fará uma associação entre o trabalho
person e event de ambos os lados em Java. O esquema do banco de dados acima não muda,
de forma que você continua possuir a multiplicidade muitos-para-muitos.
Nota
Como você pode ver, esses são mapeamentos set normais em ambos documentos de
mapeamento. Observe que os nomes das colunas em key e many-to-many estão trocados
em ambos os documentos de mapeamento. A adição mais importante feita está na função
inverse="true" no elemento set da coleção da classe Event.
20
Trabalhando com links bidirecionais
Isso significa que o Hibernate deve pegar o outro lado, a classe Person, quando precisar
encontrar informação sobre a relação entre as duas entidades. Isso será muito mais fácil de
entender quando você analisar como a relação bidirecional entre as entidades é criada.
Observe que os métodos set e get da coleção estão protegidos. Isso permite que classes e
subclasses do mesmo pacote continuem acessando os métodos, mas evita que qualquer outra
classe, que não esteja no mesmo pacote, acesse a coleção diretamente. Repita os passos para
a coleção do outro lado.
E sobre o mapeamento da função inverse? Para você, e para o Java, um link bidirecional é
simplesmente uma questão de configurar corretamente as referências de ambos os lados. O
Hibernate, entretanto, não possui informação necessária para ajustar corretamente as instruções
INSERT e UPDATE do SQL (para evitar violações de restrição) e precisa de ajuda para manipular
as associações bidirecionais de forma apropriada. Ao fazer um lado da associação com a função
inverse, você instrui o Hibernate para basicamente ignorá-lo, considerando-o uma cópia do
outro lado. Isso é o necessário para o Hibernate compreender todas as possibilidades quando
transformar um modelo de navegação bidirecional em esquema de banco de dados do SQL. As
regras que você precisa lembrar são diretas: todas as associações bidirecionais necessitam que
21
Capítulo 1. Tutorial
um lado possua a função inverse. Em uma associação de um-para-muitos, precisará ser o lado
de "muitos", já em uma associação de muitos-para-muitos você poderá selecionar qualquer lado.
package org.hibernate.tutorial.web;
// Imports
try {
// Begin unit of work
HibernateUtil.getSessionFactory().getCurrentSession().beginTransaction();
22
Processando e renderizando
O modelo que estamos aplicando neste código é chamado session-per-request. Quando uma
solicitação chega ao servlet, uma nova Session do Hibernate é aberta através da primeira
chamada para getCurrentSession() em SessionFactory. Então uma transação do banco de
dados é inicializada e todo acesso a dados deve ocorrer dentro de uma transação, não importando
se o dado é de leitura ou escrita. Não se deve utilizar o modo auto-commit em aplicações.
Nunca utilize uma nova Session do Hibernate para todas as operações de banco de dados.
Utilize uma Session do Hibernate que seja de interesse à todas as solicitações. Utilize
getCurrentSession(), para que seja vinculado automaticamente à thread atual de Java.
Agora, as possíveis ações de uma solicitação serão processadas e uma resposta HTML será
renderizada. Já chegaremos nesta parte.
// Handle actions
if ( "store".equals(request.getParameter("action")) ) {
if ( "".equals(eventTitle) || "".equals(eventDate) ) {
out.println("<b><i>Please enter event title and date.</i></b>");
}
else {
createAndStoreEvent(eventTitle, dateFormatter.parse(eventDate));
out.println("<b><i>Added event.</i></b>");
}
}
// Print page
printEventForm(out);
listEvents(out, dateFormatter);
23
Capítulo 1. Tutorial
out.close();
O estilo deste código misturado com o Java e HTML, não escalariam em um aplicativo mais
complexo, tenha em mente que estamos somente ilustrando os conceitos básicos do Hibernate
neste tutorial. O código imprime um cabeçalho e nota de rodapé em HTML. Dentro desta página,
são impressos um formulário para entrada de evento em HTML e uma lista de todos os evento
no banco de dados. O primeiro método é trivial e somente produz um HTML:
O método listEvents() utiliza a Session do Hibernate, limitado ao thread atual para executar
uma consulta:
24
Implementando e testando
theEvent.setDate(theDate);
HibernateUtil.getSessionFactory()
.getCurrentSession().save(theEvent);
}
O servlet está completo agora. Uma solicitação ao servlet será processada com uma única
Session e Transaction. Quanto antes estiver no aplicativo autônomo, maior a chance do
Hibernate vincular automaticamente estes objetos à thread atual de execução. Isto lhe dá a
liberdade para inserir seu código e acessar a SessionFactory como desejar. Geralmente,
usaríamos um diagrama mais sofisticado e moveríamos o código de acesso de dados para os
objetos de acesso dos dados (o modelo DAO). Veja o Hibernate Wiki para mais exemplos.
<servlet>
<servlet-name>Event Manager</servlet-name>
<servlet-class>org.hibernate.tutorial.web.EventManagerServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Event Manager</servlet-name>
<url-pattern>/eventmanager</url-pattern>
</servlet-mapping>
</web-app>
Para construir e implementar, chame seu diretório de projeto ant war e copie o arquivo
hibernate-tutorial.war para seu diretório Tomcat webapp.
Nota
If you do not have Tomcat installed, download it from http://tomcat.apache.org/
and follow the installation instructions. Our application requires no changes to the
standard Tomcat configuration.
25
Capítulo 1. Tutorial
1.4. Sumário
Este tutorial cobriu itens básicos de como escrever um aplicativo Hibernate autônomo simples e
um aplicativo da web pequeno. A partir do Hibernate website [http://hibernate.org] você poderá
encontrar mais tutoriais disponíveis.
26
Arquitetura
2.1. Visão Geral
O diagrama abaixo fornece uma visão de altíssimo nível da arquitetura do Hibernate:
Application
Persistent Objects
HIBERNATE
hibernate. XML Mapping
properties
Database
Unfortunately we cannot provide a detailed view of all possible runtime architectures. Hibernate is
sufficiently flexible to be used in a number of ways in many, many architectures. We will, however,
illustrate 2 specifically since they are extremes.
The "minimal" architecture has the application manage its own JDBC connections and provide
those connections to Hibernate; additionally the application manages transactions for itself. This
approach uses a minimal subset of Hibernate APIs.
27
Capítulo 2. Arquitetura
Persistent
Objects
Database
SessionFactory
Session Transaction
TransactionFactory ConnectionProvider
Database
28
Basic APIs
Here are quick discussions about some of the API objects depicted in the preceding diagrams
(you will see them again in more detail in later chapters).
SessionFactory (org.hibernate.SessionFactory)
A thread-safe, immutable cache of compiled mappings for a single
database. A factory for org.hibernate.Session instances. A client of
org.hibernate.connection.ConnectionProvider. Optionally maintains a second level
cache of data that is reusable between transactions at a process or cluster level.
Session (org.hibernate.Session)
A single-threaded, short-lived object representing a conversation between the
application and the persistent store. Wraps a JDBC java.sql.Connection. Factory
for org.hibernate.Transaction. Maintains a first level cache of persistent the
application's persistent objects and collections; this cache is used when navigating the object
graph or looking up objects by identifier.
Transaction (org.hibernate.Transaction)
(Optional) A single-threaded, short-lived object used by the application to specify atomic
units of work. It abstracts the application from the underlying JDBC, JTA or CORBA
transaction. A org.hibernate.Session might span several org.hibernate.Transactions
in some cases. However, transaction demarcation, either using the underlying API or
org.hibernate.Transaction, is never optional.
ConnectionProvider (org.hibernate.connection.ConnectionProvider)
(Optional) A factory for, and pool of, JDBC connections. It abstracts the application from
underlying javax.sql.DataSource or java.sql.DriverManager. It is not exposed to
application, but it can be extended and/or implemented by the developer.
29
Capítulo 2. Arquitetura
TransactionFactory (org.hibernate.TransactionFactory)
(Optional) A factory for org.hibernate.Transaction instances. It is not exposed to the
application, but it can be extended and/or implemented by the developer.
Extension Interfaces
O Hibernate oferece várias opções de interfaces estendidas que você pode implementar para
customizar sua camada persistente. Veja a documentação da API para maiores detalhes.
Another feature available as a JMX service is runtime Hibernate statistics. See Seção 3.4.6,
“Estatísticas do Hibernate” for more information.
30
Sessões Contextuais
The first two implementations provide a "one session - one database transaction" programming
model. This is also known and used as session-per-request. The beginning and end of a Hibernate
session is defined by the duration of a database transaction. If you use programmatic transaction
demarcation in plain JSE without JTA, you are advised to use the Hibernate Transaction API
to hide the underlying transaction system from your code. If you use JTA, you can utilize the
JTA interfaces to demarcate transactions. If you execute in an EJB container that supports CMT,
transaction boundaries are defined declaratively and you do not need any transaction or session
demarcation operations in your code. Refer to Capítulo 13, Transações e Concorrência for more
information and code examples.
31
32
Configuration
Devido ao fato do Hibernate ser projetado para operar em vários ambientes diferentes, há um
grande número de parâmetros de configuração. Felizmente, a maioria possui valores padrão
consideráveis e o Hibernate é distribuído com um arquivo hibernate.properties de exemplo
no etc/ que mostra várias opções. Apenas coloque o arquivo de exemplo no seu classpath e
personalize-o.
Uma alternativa é especificar a classe mapeada e permitir que o Hibernate encontre o documento
de mapeamento para você:
33
Capítulo 3. Configuration
Esta não é a única forma de passar as propriedades de configuração para o Hibernate. As várias
opções incluem:
Assim que você fizer algo que requeira o acesso ao banco de dados, uma conexão JDBC será
obtida a partir do pool.
Para esse trabalho, precisaremos passar algumas propriedades da conexão JDBC para o
Hibernate. Todos os nomes de propriedades Hibernate e semânticas são definidas na classe
org.hibernate.cfg.Environment. Descreveremos agora as configurações mais importantes
para a conexão JDBC.
34
Conexões JDBC
O C3P0 é um pool conexão JDBC de código aberto distribuído junto com Hibernate no diretório
lib. O Hibernate usará o próprio org.hibernate.connection.C3P0ConnectionProvider para
o pool de conexão se você configurar a propriedade hibernate.c3p0.*. Se você gostar de
usar Proxool, consulte o pacote hibernate.properties e o web site do Hibernate para mais
informações.
hibernate.connection.driver_class = org.postgresql.Driver
hibernate.connection.url = jdbc:postgresql://localhost/mydatabase
hibernate.connection.username = myuser
hibernate.connection.password = secret
hibernate.c3p0.min_size=5
hibernate.c3p0.max_size=20
hibernate.c3p0.timeout=1800
hibernate.c3p0.max_statements=50
hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
Para usar dentro de um servidor de aplicação, você deve configurar o Hibernate para obter
conexões de um servidor de aplicação javax.sql.Datasource registrado no JNDI. Você
precisará determinar pelo menos uma das seguintes propriedades:
35
Capítulo 3. Configuration
hibernate.connection.datasource = java:/comp/env/jdbc/test
hibernate.transaction.factory_class = \
org.hibernate.transaction.JTATransactionFactory
hibernate.transaction.manager_lookup_class = \
org.hibernate.transaction.JBossTransactionManagerLookup
hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
Conexões JDBC obtidas de um datasource JNDI irão automaticamente participar das transações
gerenciadas pelo recipiente no servidor de aplicação.
Você pode definir sua própria estratégia de plugin para obter conexões JDBC implementando
a interface org.hibernate.connection.ConnectionProvider e especificando sua
implementação customizada através da propriedade hibernate.connection.provider_class.
Atenção
36
Propriedades opcionais de configuração
e.g. full.classname.of.Dialect
e.g. SCHEMA_NAME
hibernate.default_catalog Qualifica no SQL gerado, os nome das tabelas
desqualificadas com catálogo dado.
e.g. CATALOG_NAME
hibernate.session_factory_name O org.hibernate.SessionFactory irá
automaticamente se ligar a este nome no JNDI
depois de ter sido criado.
e.g. jndi/composite/name
hibernate.max_fetch_depth Estabelece a "profundidade" máxima para
árvore de busca de união externa para
associações finais únicas (um para um, muitos
para um). Um 0 desativa por padrão a busca
de união externa.
37
Capítulo 3. Configuration
38
Propriedades opcionais de configuração
Nota
exemplo classname.of.BatcherFactory
hibernate.jdbc.use_scrollable_resultset Habilita o uso dos resultados de ajustes
roláveis do JDBC2 pelo Hibernate. Essa
propriedade somente é necessária quando se
usa Conexões JDBC providas pelo usuário.
Do contrário, o Hibernate os os metadados da
conexão.
39
Capítulo 3. Configuration
exemplo true|false
hibernate.connection.provider_class O nome da classe de um
org.hibernate.connection.ConnectionProvider,
do qual proverá conexões JDBC para o
Hibernate.
exemploclassname.of.ConnectionProvider
hibernate.connection.isolation Determina o nível de isolamento
de uma transação JDBC. Verifique
java.sql.Connection para valores
significativos mas note que a maior parte
dos bancos de dados não suportam todos os
isolamentos que não são padrões.
exemplo 1, 2, 4, 8
hibernate.connection.autocommit Habilita o auto-commit para conexões no pool
JDBC (não recomendado).
40
Propriedades opcionais de configuração
exemplo classname.of.CacheProvider
hibernate.cache.use_minimal_puts Otimizar operação de cachê de segundo nível
para minimizar escritas, ao custo de leituras
mais freqüentes. Esta configuração é mais útil
para cachês em cluster e, no Hibernate3, é
habilitado por padrão para implementações de
cache em cluster.
exemplo true|false
hibernate.cache.use_query_cache Habilita a cache de consultas. Mesmo assim,
consultas individuais ainda têm que ser
habilitadas para o cache.
exemplo true|false
hibernate.cache.use_second_level_cache Pode ser utilizado para desabilitar
completamente o cache de segundo nível, o
qual é habilitado por padrão para as classes
que especificam um mapeamento <cache>.
41
Capítulo 3. Configuration
exemplo classname.of.QueryCache
hibernate.cache.region_prefix Um prefixo para usar em nomes regionais de
cachê de segundo nível
exemplo prefix
hibernate.cache.use_structured_entries Força o Hibernate a armazenar dados no
cachê de segundo nível em um formato mais
humanamente amigável.
exemplo true|false
Setting used to give the name of the default
hibernate.cache.default_cache_concurrency_strategy
org.hibernate.annotations.CacheConcurrencyStrategy
to use when either @Cacheable or @Cache
is used. @Cache(strategy="..") is used to
override this default.
exemplo
classname.of.TransactionFactory
e.g. jndi/composite/name
O
hibernate.transaction.manager_lookup_class nome da classe de um
TransactionManagerLookup. Ele é requerido
quando o caching a nível JVM estiver
habilitado ou quando estivermos usando um
gerador hilo em um ambiente JTA.
42
Propriedades opcionais de configuração
exemplo org.hibernate.hql.ast.
ASTQueryTranslatorFactory ou
org.hibernate.hql.classic.
ClassicQueryTranslatorFactory
exemplo hqlLiteral=SQL_LITERAL,
hqlFunction=SQLFUNC
43
Capítulo 3. Configuration
e.g. /humans.sql,/dogs.sql
Enables the use of bytecode manipulation
hibernate.bytecode.use_reflection_optimizer
instead of runtime reflection. This is
a System-level property and cannot be
set in hibernate.cfg.xml. Reflection can
sometimes be useful when troubleshooting.
Hibernate always requires either CGLIB or
javassist even if you turn off the optimizer.
44
Busca por união externa (Outer Join Fetching)
RDBMS Dialeto
DB2 org.hibernate.dialect.DB2Dialect
PostgreSQL org.hibernate.dialect.PostgreSQLDialect
Oracle 9i org.hibernate.dialect.Oracle9iDialect
Sybase org.hibernate.dialect.SybaseDialect
SAP DB org.hibernate.dialect.SAPDBDialect
Informix org.hibernate.dialect.InformixDialect
HypersonicSQL org.hibernate.dialect.HSQLDialect
Ingres org.hibernate.dialect.IngresDialect
Progresso org.hibernate.dialect.ProgressDialect
Interbase org.hibernate.dialect.InterbaseDialect
Firebird org.hibernate.dialect.FirebirdDialect
Se seu banco de dados suporta união externa no estilo ANSI, Oracle ou Sybase, a outer join
fetching freqüentemente aumentará o desempenho limitando o número de chamadas (round trips)
para e a partir do banco de dados. No entanto, isto ao custo de possivelmente mais trabalho
desempenhado pelo próprio banco de dados. A busca por união externa (outer join fetching)
permite um gráfico completo de objetos conectados por associações muitos-para-um, um-para-
muitos, muitos-para-muitos e um-para-um para serem recuperadas em uma simples instrução
SQL SELECT.
45
Capítulo 3. Configuration
A busca por união externa pode ser desabilitada globalmente configurando a propriedade
hibernate.max_fetch_depth para 0. Um valor 1 ou maior habilita a busca por união externa para
associações um-para-um e muitos-para-um, cujos quais têm sido mapeados com fetch="join".
Isto faria com que os símbolos true e false passasem a ser traduzidos para literais inteiros no
SQL gerado.
hibernate.query.substitutions toLowercase=LOWER
3.5. Logging
O Hibernate utiliza o Simple Logging Facade for Java [http://www.slf4j.org/] (SLF4J) com o
objetivo de registrar os diversos eventos de sistema. O SLF4J pode direcionar a sua saída de
46
Implementando um NamingStrategy
logging a diversos frameworks de logging (NOP, Simple, log4j version 1.2, JDK 1.4 logging,
JCL ou logback) dependendo de sua escolha de vinculação. Com o objetivo de determinar o
seu logging, você precisará do slf4j-api.jar em seu classpatch juntamente com o arquivo
jar para a sua vinculação preferida - slf4j-log4j12.jar no caso do Log4J. Consulte o SLF4J
documentation [http://www.slf4j.org/manual.html] para maiores detalhes. Para usar o Log4j você
precisará também colocar um arquivo log4j.properties em seu classpath. Um exemplo do
arquivo de propriedades está distribuído com o Hibernate no diretório src/.
Nós recomendamos que você se familiarize-se com mensagens de log do Hibernate. Tem sido
um árduo trabalho fazer o log Hibernate tão detalhado quanto possível, sem fazê-lo ilegível. É
um dispositivo de controle de erros essencial. As categorias de log mais interessantes são as
seguintes:
Categoria Função
org.hibernate.SQL Registra todas as instruções SQL DML a medida que elas são
executadas
org.hibernate.type Registra todos os parâmetros JDBC
Registra todas as instruções SQL DDL a medida que elas são
org.hibernate.tool.hbm2ddl
executadas
org.hibernate.pretty Registra o estado de todas as entidades (máximo 20 entidades)
associadas à sessão no momento da liberação (flush).
org.hibernate.cache Registra todas as atividades de cachê de segundo nível
Registra atividades relacionada à transação
org.hibernate.transaction
Ao desenvolver aplicações com Hibernate, você deve quase sempre trabalhar com o depurador
debug habilitado para a categoria org.hibernate.SQL, ou, alternativamente, com a propriedade
hibernate.show_sql habilitada.
Você deve criar regras para a geração automaticamente de identificadores do banco de dados
a partir de identificadores Java ou para processar colunas "lógicas" e nomes de tabelas dado o
arquivo de mapeamento para nomes "físicos" de tabelas e colunas. Este recurso ajuda a reduzir a
47
Capítulo 3. Configuration
O arquivo XML de configuração deve ser encontrado na raíz do seu CLASSPATH. Veja um exemplo:
<hibernate-configuration>
48
Integração com servidores de aplicação J2EE
</session-factory>
</hibernate-configuration>
Como você pode ver, a vantagem deste enfoque é a externalização dos nomes dos arquivos
de mapeamento para configuração. O hibernate.cfg.xml também é mais conveniente
caso você tenha que ajustar o cache do Hibernate. Note que a escolha é sua em usar
hibernate.properties ou hibernate.cfg.xml, ambos são equivalentes, exceto os acima
mencionados de usar a sintaxe de XML.
• DataSources gerenciados pelo container: O Hibernate pode usar conexões JDBC gerenciadas
pelo Container e fornecidas pela JNDI. Geralmente, um TransactionManager compatível com
JTA e um ResourceManager cuidam do gerenciamento da transação (CMT), especialmente
em transações distribuídas, manipuladas através de vários DataSources. Naturalmente, você
também pode demarcar os limites das transações programaticamente (BMT) ou você poderia
querer usar a API opcional do Hibernate Transaction para esta manter seu código portável.
• JMX deployment: Se você usa um JMX servidor de aplicações capaz (ex. Jboss AS), você pode
fazer a instalação do Hibernate como um MBean controlado. Isto evita ter que iniciar uma linha
de código para construir sua SessionFactory de uma Configuration. O container iniciará
49
Capítulo 3. Configuration
Dependendo do seu ambiente, você pode ter que ajustar a opção de configuração
hibernate.connection.aggressive_release para verdadeiro ( true ), se seu servidor de
aplicações lançar exeções "retenção de conexão".
Para manter seu código portável entre estes dois (e outros) ambientes, recomendamos a API
Hibernate Transaction, que envolve e esconde o sistema subjacente. Você tem que especificar
uma classe construtora para instâncias Transaction ajustando a propriedade de configuração
do hibernate.transaction.factory_class.
org.hibernate.transaction.JDBCTransactionFactory
delega as transações (JDBC) para bases de dados (Padrão)
org.hibernate.transaction.JTATransactionFactory
delega para uma transação à um container gerenciado se uma transação existente estiver de
acordo neste contexto (ex: método bean de sessão EJB). No entanto, uma nova transação
será iniciada e serão usadas transações controladas por um bean.
org.hibernate.transaction.CMTTransactionFactory
delega para um container gerenciador de transações JTA
Você também pode definir suas próprias estratégias de transação (para um serviço de transação
CORBA, por exemplo).
Algumas características no Hibernate (ex., o cache de segundo nível, sessões contextuais com
JTA, etc.) requerem acesso a JTA TransactionManager em um ambiente controlado. Em um
servidor de aplicação você tem que especificar como o Hibernate pode obter uma referência para
a TransactionManager, pois o J2EE não padroniza um mecanismo simples:
50
SessionFactory vinculada à JNDI
org.hibernate.transaction.OrionTransactionManagerLookup Orion
org.hibernate.transaction.ResinTransactionManagerLookup Resin
org.hibernate.transaction.JOTMTransactionManagerLookup JOTM
org.hibernate.transaction.JOnASTransactionManagerLookup JOnAS
org.hibernate.transaction.JRun4TransactionManagerLookup JRun4
org.hibernate.transaction.BESTransactionManagerLookup Borland ES
JBoss TS used
org.hibernate.transaction.JBossTSStandaloneTransactionManagerLookup
standalone (ie. outside
JBoss AS and a JNDI
environment generally).
Known to work for
org.jboss.jbossts:jbossjta:4.11.0.Final
It is recommended that you bind the SessionFactory to JNDI in a managed environment and
use a static singleton otherwise. To shield your application code from these details, we also
recommend to hide the actual lookup code for a SessionFactory in a helper class, such as
HibernateUtil.getSessionFactory(). Note that such a class is also a convenient way to
startup Hibernate—see chapter 1.
51
Capítulo 3. Configuration
<?xml version="1.0"?>
<server>
<mbean code="org.hibernate.jmx.HibernateService"
name="jboss.jca:service=HibernateFactory,name=HibernateFactory">
52
implementação JMX
<attribute name="MaximumFetchDepth">5</attribute>
</mbean>
</server>
53
54
Classes Persistentes
Persistent classes are classes in an application that implement the entities of the business problem
(e.g. Customer and Order in an E-commerce application). The term "persistent" here means that
the classes are able to be persisted, not that they are in the persistent state (see Seção 11.1,
“Estado dos objetos no Hibernate” for discussion).
Hibernate works best if these classes follow some simple rules, also known as the Plain Old
Java Object (POJO) programming model. However, none of these rules are hard requirements.
Indeed, Hibernate assumes very little about the nature of your persistent objects. You can express
a domain model in other ways (using trees of java.util.Map instances, for example).
package eg;
import java.util.Set;
import java.util.Date;
55
Capítulo 4. Classes Persistentes
As quatro regras principais das classes persistentes são descritas em maiores detalhes nas
seguintes seções.
Cat has a no-argument constructor. All persistent classes must have a default
constructor (which can be non-public) so that Hibernate can instantiate them using
java.lang.reflect.Constructor.newInstance(). It is recommended that this constructor be
defined with at least package visibility in order for runtime proxy generation to work properly.
56
Provide an identifier property
Nota
Historically this was considered option. While still not (yet) enforced, this should
be considered a deprecated feature as it will be completely required to provide a
identifier property in an upcoming release.
Cat has a property named id. This property maps to the primary key column(s) of the
underlying database table. The type of the identifier property can be any "basic" type (see ???).
See Seção 9.4, “Componentes como identificadores compostos” for information on mapping
composite (multi-column) identifiers.
Nota
A central feature of Hibernate, proxies (lazy loading), depends upon the persistent class being
either non-final, or the implementation of an interface that declares all public methods. You can
persist final classes that do not implement an interface with Hibernate; you will not, however,
be able to use proxies for lazy association fetching which will ultimately limit your options for
performance tuning. To persist a final class which does not implement a "full" interface you must
disable proxy generation. See Exemplo 4.2, “Disabling proxies in hbm.xml” and Exemplo 4.3,
“Disabling proxies in annotations”.
57
Capítulo 4. Classes Persistentes
If the final class does implement a proper interface, you could alternatively tell Hibernate to use
the interface instead when generating the proxies. See Exemplo 4.4, “Proxying an interface in
hbm.xml” and Exemplo 4.5, “Proxying an interface in annotations”.
You should also avoid declaring public final methods as this will again limit the ability to
generate proxies from this class. If you want to use a class with public final methods, you
must explicitly disable proxying. Again, see Exemplo 4.2, “Disabling proxies in hbm.xml” and
Exemplo 4.3, “Disabling proxies in annotations”.
Properties need not be declared public. Hibernate can persist a property declared with package,
protected or private visibility as well.
package eg;
58
Implementando equals() e hashCode()
...
public boolean equals(Object other) {
if (this == other) return true;
if ( !(other instanceof Cat) ) return false;
return true;
}
59
Capítulo 4. Classes Persistentes
A business key does not have to be as solid as a database primary key candidate (see
Seção 13.1.3, “Considerando a identidade do objeto”). Immutable or unique properties are usually
good candidates for a business key.
Nota
The following features are currently considered experimental and may change in
the near future.
Entidades persistentes não precisam ser representadas como classes POJO ou como objetos
JavaBeans em tempo de espera. O Hibernate também suporta modelos dinâmicos (usando Maps
de Maps em tempo de execução) e a representação de entidades como árvores DOM4J. Com
esta abordagem, você não escreve classes persistes, somente arquivos de mapeamentos.
By default, Hibernate works in normal POJO mode. You can set a default entity representation
mode for a particular SessionFactory using the default_entity_mode configuration option (see
Tabela 3.3, “Propriedades de Configuração do Hibernate”).
<hibernate-mapping>
<class entity-name="Customer">
<id name="id"
type="long"
column="ID">
<generator class="sequence"/>
</id>
<property name="name"
column="NAME"
type="string"/>
<property name="address"
column="ADDRESS"
60
Modelos dinâmicos
type="string"/>
<many-to-one name="organization"
column="ORGANIZATION_ID"
class="Organization"/>
<bag name="orders"
inverse="true"
lazy="false"
cascade="all">
<key column="CUSTOMER_ID"/>
<one-to-many class="Order"/>
</bag>
</class>
</hibernate-mapping>
Note que embora as associações sejam declaradas utilizando nomes de classe, o tipo alvo de
uma associação pode também ser uma entidade dinâmica, ao invés de um POJO.
Após ajustar o modo de entidade padrão para dynamic-map para a SessionFactory, você poderá
trabalhar com Maps de Maps no período de execução:
Session s = openSession();
Transaction tx = s.beginTransaction();
// Create a customer
Map david = new HashMap();
david.put("name", "David");
// Create an organization
Map foobar = new HashMap();
foobar.put("name", "Foobar Inc.");
// Link both
david.put("organization", foobar);
// Save both
s.save("Customer", david);
s.save("Organization", foobar);
tx.commit();
s.close();
Modos de representação de entidade podem ser também ajustados para base por Session:
61
Capítulo 4. Classes Persistentes
// Create a customer
Map david = new HashMap();
david.put("name", "David");
dynamicSession.save("Customer", david);
...
dynamicSession.flush();
dynamicSession.close()
...
// Continue on pojoSession
Por favor, note que a chamada para a getSession() usando um EntityMode está na API
de Session e não na SessionFactory. Dessa forma, a nova Session compartilha a conexão,
transação e outra informação de contexto JDBC adjacente. Isto significa que você não precisará
chamar flush() e close() na Session secundária, e também deixar a transação e o manuseio
da conexão para a unidade primária do trabalho.
More information about the XML representation capabilities can be found in Capítulo 20,
Mapeamento XML.
4.5. Tuplizadores
org.hibernate.tuple.Tuplizer and its sub-interfaces are responsible for managing
a particular representation of a piece of data given that representation's
org.hibernate.EntityMode. If a given piece of data is thought of as a data structure, then a
tuplizer is the thing that knows how to create such a data structure, how to extract values from such
a data structure and how to inject values into such a data structure. For example, for the POJO
entity mode, the corresponding tuplizer knows how create the POJO through its constructor. It
also knows how to access the POJO properties using the defined property accessors.
Users can also plug in their own tuplizers. Perhaps you require that java.util.Map
implementation other than java.util.HashMap be used while in the dynamic-map entity-mode.
Or perhaps you need to define a different proxy generation strategy than the one used by default.
Both would be achieved by defining a custom tuplizer implementation. Tuplizer definitions are
attached to the entity or component mapping they are meant to manage. Going back to the
example of our Customer entity, Exemplo 4.6, “Specify custom tuplizers in annotations” shows
how to specify a custom org.hibernate.tuple.entity.EntityTuplizer using annotations
while Exemplo 4.7, “Specify custom tuplizers in hbm.xml” shows how to do the same in hbm.xml
62
EntityNameResolvers
@Entity
@Tuplizer(impl = DynamicEntityTuplizer.class)
public interface Cuisine {
@Id
@GeneratedValue
public Long getId();
public void setId(Long id);
@Tuplizer(impl = DynamicComponentTuplizer.class)
public Country getCountry();
public void setCountry(Country country);
}
<hibernate-mapping>
<class entity-name="Customer">
<!--
Override the dynamic-map entity-mode
tuplizer for the customer entity
-->
<tuplizer entity-mode="dynamic-map"
class="CustomMapTuplizerImpl"/>
4.6. EntityNameResolvers
org.hibernate.EntityNameResolver is a contract for resolving the entity name of a given
entity instance. The interface defines a single method resolveEntityName which is passed
the entity instance and is expected to return the appropriate entity name (null is allowed and
would indicate that the resolver does not know how to resolve the entity name of the given
entity instance). Generally speaking, an org.hibernate.EntityNameResolver is going to be
most useful in the case of dynamic models. One example might be using proxied interfaces as
your domain model. The hibernate test suite has an example of this exact style of usage under
the org.hibernate.test.dynamicentity.tuplizer2. Here is some of the code from that package for
illustration.
63
Capítulo 4. Classes Persistentes
/**
* A very trivial JDK Proxy InvocationHandler implementation where we proxy an
* interface as the domain model and simply store persistent state in an internal
* Map. This is an extremely trivial example meant only for illustration.
*/
public final class DataProxyHandler implements InvocationHandler {
private String entityName;
private HashMap data = new HashMap();
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
if ( methodName.startsWith( "set" ) ) {
String propertyName = methodName.substring( 3 );
data.put( propertyName, args[0] );
}
else if ( methodName.startsWith( "get" ) ) {
String propertyName = methodName.substring( 3 );
return data.get( propertyName );
}
else if ( "toString".equals( methodName ) ) {
return entityName + "#" + data.get( "Id" );
}
else if ( "hashCode".equals( methodName ) ) {
return new Integer( this.hashCode() );
}
return null;
}
64
EntityNameResolvers
/**
* The EntityNameResolver implementation.
*
* IMPL NOTE : An EntityNameResolver really defines a strategy for how entity names
* should be resolved. Since this particular impl can handle resolution for all of our
* entities we want to take advantage of the fact that SessionFactoryImpl keeps these
* in a Set so that we only ever have one instance registered. Why? Well, when it
* comes time to resolve an entity name, Hibernate must iterate over all the registered
* resolvers. So keeping that number down helps that process be as speedy as possible.
* Hence the equals and hashCode implementations as is
*/
public class MyEntityNameResolver implements EntityNameResolver {
public static final MyEntityNameResolver INSTANCE = new MyEntityNameResolver();
...
65
Capítulo 4. Classes Persistentes
66
Mapeamento O/R Básico
5.1. Declaração de mapeamento
Object/relational mappings can be defined in three approaches:
Annotations are split in two categories, the logical mapping annotations (describing the object
model, the association between two entities etc.) and the physical mapping annotations
(describing the physical schema, tables, columns, indexes, etc). We will mix annotations from
both categories in the following code examples.
JPA annotations are in the javax.persistence.* package. Hibernate specific extensions are
in org.hibernate.annotations.*. You favorite IDE can auto-complete annotations and their
attributes for you (even without a specific "JPA" plugin, since JPA annotations are plain Java 5
annotations).
package eg;
@Entity
@Table(name="cats") @Inheritance(strategy=SINGLE_TABLE)
@DiscriminatorValue("C") @DiscriminatorColumn(name="subclass", discriminatorType=CHAR)
public class Cat {
@Id @GeneratedValue
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
private Integer id;
@org.hibernate.annotations.Type(type="eg.types.ColorUserType")
@NotNull @Column(updatable=false)
public ColorType getColor() { return color; }
public void setColor(ColorType color) { this.color = color; }
private ColorType color;
@NotNull @Column(updatable=false)
67
Capítulo 5. Mapeamento O/R Básico
@NotNull @Column(updatable=false)
public Integer getLitterId() { return litterId; }
public void setLitterId(Integer litterId) { this.litterId = litterId; }
private Integer litterId;
@OneToMany(mappedBy="mother") @OrderBy("litterId")
public Set<Cat> getKittens() { return kittens; }
public void setKittens(Set<Cat> kittens) { this.kittens = kittens; }
private Set<Cat> kittens = new HashSet<Cat>();
}
@Entity @DiscriminatorValue("D")
public class DomesticCat extends Cat {
@Entity
public class Dog { ... }
The legacy hbm.xml approach uses an XML schema designed to be readable and hand-editable.
The mapping language is Java-centric, meaning that mappings are constructed around persistent
class declarations and not table declarations.
Note que, embora muitos usuários do Hibernate escolham gravar o XML manualmente, existem
diversas ferramentas para gerar o documento de mapeamento, incluindo o XDoclet Middlegen
e AndroMDA.
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="eg">
<class name="Cat"
table="cats"
discriminator-value="C">
<id name="id">
<generator class="native"/>
</id>
68
Declaração de mapeamento
<discriminator column="subclass"
type="character"/>
<property name="weight"/>
<property name="birthdate"
type="date"
not-null="true"
update="false"/>
<property name="color"
type="eg.types.ColorUserType"
not-null="true"
update="false"/>
<property name="sex"
not-null="true"
update="false"/>
<property name="litterId"
column="litterId"
update="false"/>
<many-to-one name="mother"
column="mother_id"
update="false"/>
<set name="kittens"
inverse="true"
order-by="litter_id">
<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>
We will now discuss the concepts of the mapping documents (both annotations and XML). We
will only describe, however, the document elements and attributes that are used by Hibernate at
runtime. The mapping document also contains some extra optional attributes and elements that
affect the database schemas exported by the schema export tool (for example, the not-null
attribute).
69
Capítulo 5. Mapeamento O/R Básico
5.1.1. Entity
An entity is a regular Java object (aka POJO) which will be persisted by Hibernate.
@Entity
public class Flight implements Serializable {
Long id;
@Id
public Long getId() { return id; }
That's pretty much it, the rest is optional. There are however any options to tweak your entity
mapping, let's explore them.
@Table lets you define the table the entity will be persisted into. If undefined, the table name is
the unqualified class name of the entity. You can also optionally define the catalog, the schema
as well as unique constraints on the table.
@Entity
@Table(name="TBL_FLIGHT",
schema="AIR_COMMAND",
uniqueConstraints=
@UniqueConstraint(
name="flight_number",
columnNames={"comp_prefix", "flight_number"} ) )
public class Flight implements Serializable {
@Column(name="comp_prefix")
public String getCompagnyPrefix() { return companyPrefix; }
@Column(name="flight_number")
public String getNumber() { return number; }
}
The constraint name is optional (generated if left undefined). The column names composing the
constraint correspond to the column names as defined before the Hibernate NamingStrategy is
applied.
@Entity.name lets you define the shortcut name of the entity you can used in JP-QL and HQL
queries. It defaults to the unqualified class name of the class.
Hibernate goes beyond the JPA specification and provide additional configurations. Some of them
are hosted on @org.hibernate.annotations.Entity:
• dynamicInsert / dynamicUpdate (defaults to false): specifies that INSERT / UPDATE SQL should
be generated at runtime and contain only the columns whose values are not null. The dynamic-
70
Entity
update and dynamic-insert settings are not inherited by subclasses. Although these settings
can increase performance in some cases, they can actually decrease performance in others.
• selectBeforeUpdate (defaults to false): specifies that Hibernate should never perform an SQL
UPDATE unless it is certain that an object is actually modified. Only when a transient object
has been associated with a new session using update(), will Hibernate perform an extra SQL
SELECT to determine if an UPDATE is actually required. Use of select-before-update will
usually decrease performance. It is useful to prevent a database update trigger being called
unnecessarily if you reattach a graph of detached instances to a Session.
• persister: specifies a custom ClassPersister. The persister attribute lets you customize
the persistence strategy used for the class. You can, for example, specify your own
subclass of org.hibernate.persister.EntityPersister, or you can even provide a
completely new implementation of the interface org.hibernate.persister.ClassPersister
that implements, for example, persistence via stored procedure calls, serialization to flat files
or LDAP. See org.hibernate.test.CustomPersister for a simple example of "persistence"
to a Hashtable.
• optimisticLock (defaults to VERSION): determines the optimistic locking strategy. If you enable
dynamicUpdate, you will have a choice of optimistic locking strategies:
Nós realmente recomendamos que você utilize as colunas de versão/timestamp para o bloqueio
otimista com o Hibernate. Esta é a melhor estratégia em relação ao desempenho e é a única
estratégia que trata corretamente as modificações efetuadas em instâncias desconectadas (por
exemplo, quando Session.merge() é utilizado).
71
Capítulo 5. Mapeamento O/R Básico
Dica
Some entities are not mutable. They cannot be updated or deleted by the application. This allows
Hibernate to make some minor performance optimizations.. Use the @Immutable annotation.
You can also alter how Hibernate deals with lazy initialization for this class. On @Proxy, use
lazy=false to disable lazy fetching (not recommended). You can also specify an interface to use
for lazy initializing proxies (defaults to the class itself): use proxyClass on @Proxy. Hibernate will
initially return proxies (Javassist or CGLIB) that implement the named interface. The persistent
object will load when a method of the proxy is invoked. See "Initializing collections and proxies"
below.
@BatchSize specifies a "batch size" for fetching instances of this class by identifier. Not yet loaded
instances are loaded batch-size at a time (default 1).
You can specific an arbitrary SQL WHERE condition to be used when retrieving objects of this
class. Use @Where for that.
In the same vein, @Check lets you define an SQL expression used to generate a multi-row check
constraint for automatic schema generation.
There is no difference between a view and a base table for a Hibernate mapping. This is
transparent at the database level, although some DBMS do not support views properly, especially
with updates. Sometimes you want to use a view, but you cannot create one in the database (i.e.
with a legacy schema). In this case, you can map an immutable and read-only entity to a given
SQL subselect expression using @org.hibernate.annotations.Subselect:
@Entity
@Subselect("select item.name, max(bid.amount), count(*) "
+ "from item "
+ "join bid on bid.item_id = item.id "
+ "group by item.name")
@Synchronize( {"item", "bid"} ) //tables impacted
public class Summary {
@Id
public String getId() { return id; }
...
}
Declare as tabelas para sincronizar com esta entidade, garantindo que a auto-liberação
ocorra corretamente, e que as consultas para esta entidade derivada não retornem dados
desatualizados. O <subselect> está disponível tanto como um atributo como um elemento
mapeado aninhado.
72
Entity
We will now explore the same options using the hbm.xml structure. You can declare a persistent
class using the class element. For example:
<class
name="ClassName"
table="tableName"
discriminator-value="discriminator_value"
mutable="true|false"
schema="owner"
catalog="catalog"
proxy="ProxyInterface"
dynamic-update="true|false"
dynamic-insert="true|false"
select-before-update="true|false"
polymorphism="implicit|explicit"
persister="PersisterClass"
batch-size="N"
optimistic-lock="none|version|dirty|all"
lazy="true|false" (16)
entity-name="EntityName" (17)
check="arbitrary sql check condition" (18)
rowid="rowid" (19)
subselect="SQL expression" (20)
abstract="true|false" (21)
node="element-name"
/>
name (opcional): O nome da classe Java inteiramente qualificado da classe persistente (ou
interface). Se a função é ausente, assume-se que o mapeamento é para entidades não-
POJO.
table (opcional – padrão para nomes de classes não qualificadas): O nome da sua tabela
do banco de dados.
discriminator-value (opcional – padrão para o nome da classe): Um valor que distingue
subclasses individuais, usadas para o comportamento polimórfico. Valores aceitos incluem
null e not null.
mutable (opcional - valor padrão true): Especifica quais instâncias da classe são (ou não)
mutáveis.
schema (opcional): Sobrepõe o nome do esquema especificado pelo elemento raíz
<hibernate-mapping>.
catalog (opcional): Sobrepõe o nome do catálogo especificado pelo elemento raíz
<hibernate-mapping>.
proxy (opcional): Especifica uma interface para ser utilizada pelos proxies de inicialização
lazy. Você pode especificar o nome da própria classe.
73
Capítulo 5. Mapeamento O/R Básico
dynamic-update (opcional, valor padrão false): Especifica que o SQL de UPDATE deve
ser gerado em tempo de execução e conter apenas aquelas colunas cujos valores foram
alterados.
dynamic-insert (opcional, valor padrão falso): Especifica que o SQL de INSERT deve ser
gerado em tempo de execução e conter apenas aquelas colunas cujos valores não estão
nulos.
select-before-update (opcional, valor padrão false): Especifica que o Hibernate nunca
deve executar um SQL de UPDATE a não ser que seja certo que um objeto está atualmente
modificado. Em certos casos (na verdade, apenas quando um objeto transiente foi associado
a uma nova sessão utilizando update()), isto significa que o Hibernate irá executar uma
instrução SQL de SELECT adicional para determinar se um UPDATE é necessário nesse
momento.
polymorphisms (optional - defaults to implicit): determines whether implicit or explicit
query polymorphisms is used.
where (opicional): Especifica um comando SQL WHERE arbitrário para ser usado quando da
recuperação de objetos desta classe.
persister (opcional): Especifica uma ClassPersister customizada.
batch-size (opcional, valor padrão 1) Especifica um "tamanho de lote" para a recuperação
de instâncias desta classe pela identificação.
optimistic-lock (opcional, valor padrão version): Determina a estratégia de bloqueio.
16 lazy (opcional): A recuperação lazy pode ser completamente desabilitada, ajustando
lazy="false".
17 entity-name (optional - defaults to the class name): Hibernate3 allows a class to be
mapped multiple times, potentially to different tables. It also allows entity mappings that
are represented by Maps or XML at the Java level. In these cases, you should provide an
explicit arbitrary name for the entity. See Seção 4.4, “Modelos dinâmicos” and Capítulo 20,
Mapeamento XML for more information.
18 check (opcional): Uma expressão SQL utilizada para gerar uma restrição de verificação de
múltiplas linhas para a geração automática do esquema.
19 rowid (opcional): O Hibernate poder usar as então chamadas ROWIDs em bancos de dados
que a suportam. Por exemplo, no Oracle, o Hibernate pode utilizar a coluna extra rowid
para atualizações mais rápidas se você configurar esta opção para rowid. Um ROWID
é uma implementação que representa de maneira detalhada a localização física de uma
determinada tuple armazenada.
20 subselect (opcional): Mapeia uma entidade imutável e somente de leitura para um
subconjunto do banco de dados. Útil se você quiser ter uma visão, ao invés de uma tabela.
Veja abaixo para mais informações.
21 abstract (opcional): Utilizada para marcar superclasses abstratas em hierarquias <union-
subclass>.
É perfeitamente aceitável uma classe persitente nomeada ser uma interface. Você deverá então
declarar as classes implementadas desta interface utilizando o elemento <subclass>. Você pode
persistir qualquer classe interna estática. Você deverá especificar o nome da classe usando a
forma padrão, por exemplo: eg.Foo$Bar.
74
Identifiers
<class name="Summary">
<subselect>
select item.name, max(bid.amount), count(*)
from item
join bid on bid.item_id = item.id
group by item.name
</subselect>
<synchronize table="item"/>
<synchronize table="bid"/>
<id name="name"/>
...
</class>
5.1.2. Identifiers
Mapped classes must declare the primary key column of the database table. Most classes will
also have a JavaBeans-style property holding the unique identifier of an instance.
@Entity
public class Person {
@Id Integer getId() { ... }
...
}
In hbm.xml, use the <id> element which defines the mapping from that property to the primary
key column.
<id
name="propertyName"
type="typename"
column="column_name"
unsaved-value="null|any|none|undefined|id_value"
access="field|property|ClassName">
node="element-name|@attribute-name|element/@attribute|."
<generator class="generatorClass"/>
</id>
75
Capítulo 5. Mapeamento O/R Básico
column (opcional – padrão para o nome da propridade): O nome coluna chave primária.
unsaved-value (opcional - padrão para um valor "sensível"): O valor da propriedade de
identificação que indica que a instância foi novamente instanciada (unsaved), diferenciando
de instâncias desconectadas que foram salvas ou carregadas em uma sessão anterior.
access (opcional - padrão para property): A estratégia que o Hiberante deve utilizar para
acessar o valor da propriedade.
Se a função name não for declarada, considera-se que a classe não tem a propriedade de
identificação.
The unsaved-value attribute is almost never needed in Hibernate3 and indeed has no
corresponding element in annotations.
You can also declare the identifier as a composite identifier. This allows access to legacy data
with composite keys. Its use is strongly discouraged for anything else.
• use a component type to represent the identifier and map it as a property in the entity: you then
annotated the property as @EmbeddedId. The component type has to be Serializable.
• map multiple properties as @Id properties: the identifier type is then the entity class itself and
needs to be Serializable. This approach is unfortunately not standard and only supported
by Hibernate.
• map multiple properties as @Id properties and declare an external class to be the identifier
type. This class, which needs to be Serializable, is declared on the entity via the @IdClass
annotation. The identifier type must contain the same properties as the identifier properties of
the entity: each property name must be the same, its type must be the same as well if the entity
property is of a basic type, its type must be the type of the primary key of the associated entity
if the entity property is an association (either a @OneToOne or a @ManyToOne).
As you can see the last case is far from obvious. It has been inherited from the dark ages of EJB
2 for backward compatibilities and we recommend you not to use it (for simplicity sake).
@Entity
class User {
@EmbeddedId
@AttributeOverride(name="firstName", column=@Column(name="fld_firstname")
76
Identifiers
UserId id;
Integer age;
}
@Embeddable
class UserId implements Serializable {
String firstName;
String lastName;
}
You can notice that the UserId class is serializable. To override the column mapping, use
@AttributeOverride.
@Entity
class Customer {
@EmbeddedId CustomerId id;
boolean preferredCustomer;
@MapsId("userId")
@JoinColumns({
@JoinColumn(name="userfirstname_fk", referencedColumnName="firstName"),
@JoinColumn(name="userlastname_fk", referencedColumnName="lastName")
})
@OneToOne User user;
}
@Embeddable
class CustomerId implements Serializable {
UserId userId;
String customerNumber;
@Entity
class User {
@EmbeddedId UserId id;
Integer age;
}
@Embeddable
class UserId implements Serializable {
String firstName;
String lastName;
In the embedded id object, the association is represented as the identifier of the associated
entity. But you can link its value to a regular association in the entity via the @MapsId annotation.
The @MapsId value correspond to the property name of the embedded id object containing
77
Capítulo 5. Mapeamento O/R Básico
the associated entity's identifier. In the database, it means that the Customer.user and the
CustomerId.userId properties share the same underlying column (user_fk in this case).
Dica
The component type used as identifier must implement equals() and hashCode().
In practice, your code only sets the Customer.user property and the user id value is copied by
Hibernate into the CustomerId.userId property.
Atenção
The id value can be copied as late as flush time, don't rely on it until after flush time.
While not supported in JPA, Hibernate lets you place your association directly in the embedded
id component (instead of having to use the @MapsId annotation).
@Entity
class Customer {
@EmbeddedId CustomerId id;
boolean preferredCustomer;
}
@Embeddable
class CustomerId implements Serializable {
@OneToOne
@JoinColumns({
@JoinColumn(name="userfirstname_fk", referencedColumnName="firstName"),
@JoinColumn(name="userlastname_fk", referencedColumnName="lastName")
})
User user;
String customerNumber;
@Entity
class User {
@EmbeddedId UserId id;
Integer age;
}
@Embeddable
class UserId implements Serializable {
String firstName;
String lastName;
78
Identifiers
<composite-id
name="propertyName"
class="ClassName"
mapped="true|false"
access="field|property|ClassName"
node="element-name|.">
<class name="User">
<composite-id name="id" class="UserId">
<key-property name="firstName" column="fld_firstname"/>
<key-property name="lastName"/>
</composite-id>
</class>
<class name="Customer">
<composite-id name="id" class="CustomerId">
<key-property name="firstName" column="userfirstname_fk"/>
<key-property name="lastName" column="userfirstname_fk"/>
<key-property name="customerNumber"/>
</composite-id>
<property name="preferredCustomer"/>
<many-to-one name="user">
<column name="userfirstname_fk" updatable="false" insertable="false"/>
<column name="userlastname_fk" updatable="false" insertable="false"/>
</many-to-one>
</class>
<class name="User">
<composite-id name="id" class="UserId">
<key-property name="firstName"/>
<key-property name="lastName"/>
</composite-id>
<property name="age"/>
</class>
79
Capítulo 5. Mapeamento O/R Básico
• the order of the properties (and column) matters. It must be the same between the association
and the primary key of the associated entity
• the many to one uses the same columns as the primary key and thus must be marked as read
only (insertable and updatable to false).
• unlike with @MapsId, the id value of the associated entity is not transparently copied, check the
foreign id generator for more information.
The last example shows how to map association directly in the embedded id component.
<class name="Customer">
<composite-id name="id" class="CustomerId">
<key-many-to-one name="user">
<column name="userfirstname_fk"/>
<column name="userlastname_fk"/>
</key-many-to-one>
<key-property name="customerNumber"/>
</composite-id>
<property name="preferredCustomer"/>
</class>
<class name="User">
<composite-id name="id" class="UserId">
<key-property name="firstName"/>
<key-property name="lastName"/>
</composite-id>
<property name="age"/>
</class>
This is the recommended approach to map composite identifier. The following options should not
be considered unless some constraint are present.
Another, arguably more natural, approach is to place @Id on multiple properties of your entity.
This approach is only supported by Hibernate (not JPA compliant) but does not require an extra
embeddable component.
@Entity
class Customer implements Serializable {
@Id @OneToOne
@JoinColumns({
@JoinColumn(name="userfirstname_fk", referencedColumnName="firstName"),
@JoinColumn(name="userlastname_fk", referencedColumnName="lastName")
})
User user;
80
Identifiers
boolean preferredCustomer;
@Entity
class User {
@EmbeddedId UserId id;
Integer age;
}
@Embeddable
class UserId implements Serializable {
String firstName;
String lastName;
In this case Customer is its own identifier representation: it must implement Serializable and
must implement equals() and hashCode().
<class name="Customer">
<composite-id>
<key-many-to-one name="user">
<column name="userfirstname_fk"/>
<column name="userlastname_fk"/>
</key-many-to-one>
<key-property name="customerNumber"/>
</composite-id>
<property name="preferredCustomer"/>
</class>
<class name="User">
<composite-id name="id" class="UserId">
<key-property name="firstName"/>
<key-property name="lastName"/>
</composite-id>
<property name="age"/>
</class>
@IdClass on an entity points to the class (component) representing the identifier of the class. The
properties marked @Id on the entity must have their corresponding property on the @IdClass. The
return type of search twin property must be either identical for basic properties or must correspond
to the identifier class of the associated entity for an association.
81
Capítulo 5. Mapeamento O/R Básico
Atenção
This approach is inherited from the EJB 2 days and we recommend against its use.
But, after all it's your application and Hibernate supports it.
@Entity
@IdClass(CustomerId.class)
class Customer implements Serializable {
@Id @OneToOne
@JoinColumns({
@JoinColumn(name="userfirstname_fk", referencedColumnName="firstName"),
@JoinColumn(name="userlastname_fk", referencedColumnName="lastName")
})
User user;
boolean preferredCustomer;
}
@Entity
class User {
@EmbeddedId UserId id;
Integer age;
@Embeddable
class UserId implements Serializable {
String firstName;
String lastName;
Customer and CustomerId do have the same properties customerNumber as well as user.
CustomerId must be Serializable and implement equals() and hashCode().
While not JPA standard, Hibernate let's you declare the vanilla associated property in the
@IdClass.
@Entity
@IdClass(CustomerId.class)
82
Identifiers
boolean preferredCustomer;
}
@Entity
class User {
@EmbeddedId UserId id;
Integer age;
@Embeddable
class UserId implements Serializable {
String firstName;
String lastName;
}
This feature is of limited interest though as you are likely to have chosen the @IdClass approach
to stay JPA compliant or you have a quite twisted mind.
<class name="Customer">
<composite-id class="CustomerId" mapped="true">
<key-many-to-one name="user">
<column name="userfirstname_fk"/>
<column name="userlastname_fk"/>
</key-many-to-one>
<key-property name="customerNumber"/>
</composite-id>
<property name="preferredCustomer"/>
</class>
<class name="User">
<composite-id name="id" class="UserId">
<key-property name="firstName"/>
<key-property name="lastName"/>
</composite-id>
83
Capítulo 5. Mapeamento O/R Básico
<property name="age"/>
</class>
Hibernate offers various generation strategies, let's explore the most common ones first that
happens to be standardized by JPA:
• IDENTITY: supports identity columns in DB2, MySQL, MS SQL Server, Sybase and
HypersonicSQL. The returned identifier is of type long, short or int.
• AUTO: selects IDENTITY, SEQUENCE or TABLE depending upon the capabilities of the underlying
database.
Importante
We recommend all new projects to use the new enhanced identifier generators.
They are deactivated by default for entities using annotations but can be activated
using hibernate.id.new_generator_mappings=true. These new generators
are more efficient and closer to the JPA 2 specification semantic.
However they are not backward compatible with existing Hibernate based
application (if a sequence or a table is used for id generation). See XXXXXXX ???
for more information on how to activate them.
To mark an id property as generated, use the @GeneratedValue annotation. You can specify the
strategy used (default to AUTO) by setting strategy.
@Entity
public class Customer {
@Id @GeneratedValue
Integer getId() { ... };
}
@Entity
public class Invoice {
84
Identifiers
@Id @GeneratedValue(strategy=GenerationType.IDENTITY)
Integer getId() { ... };
}
• catalog / schema:
• allocationSize: the amount to increment by when allocating id numbers from the generator
• uniqueConstraints: any potential column constraint on the table containing the ids
To link a table or sequence generator definition with an actual generated property, use the same
name in both the definition name and the generator value generator as shown below.
@Id
@GeneratedValue(
strategy=GenerationType.SEQUENCE,
generator="SEQ_GEN")
@javax.persistence.SequenceGenerator(
name="SEQ_GEN",
sequenceName="my_sequence",
allocationSize=20
)
public Integer getId() { ... }
The scope of a generator definition can be the application or the class. Class-defined generators
are not visible outside the class and can override application level generators. Application level
generators are defined in JPA's XML deployment descriptors (see XXXXXX ???):
<table-generator name="EMP_GEN"
table="GENERATOR_TABLE"
pk-column-name="key"
value-column-name="hi"
pk-column-value="EMP"
85
Capítulo 5. Mapeamento O/R Básico
allocation-size="20"/>
@javax.persistence.TableGenerator(
name="EMP_GEN",
table="GENERATOR_TABLE",
pkColumnName = "key",
valueColumnName = "hi"
pkColumnValue="EMP",
allocationSize=20
)
<sequence-generator name="SEQ_GEN"
sequence-name="my_sequence"
allocation-size="20"/>
@javax.persistence.SequenceGenerator(
name="SEQ_GEN",
sequenceName="my_sequence",
allocationSize=20
)
If a JPA XML descriptor (like META-INF/orm.xml) is used to define the generators, EMP_GEN and
SEQ_GEN are application level generators.
Nota
Package level definition is not supported by the JPA specification. However, you
can use the @GenericGenerator at the package level (see ???).
These are the four standard JPA generators. Hibernate goes beyond that and provide additional
generators or additional options as we will see below. You can also write your own custom identifier
generator by implementing org.hibernate.id.IdentifierGenerator.
To define a custom generator, use the @GenericGenerator annotation (and its plural counter part
@GenericGenerators) that describes the class of the identifier generator or its short cut name
(as described below) and a list of key/value parameters. When using @GenericGenerator and
assigning it via @GeneratedValue.generator, the @GeneratedValue.strategy is ignored: leave
it blank.
@Id @GeneratedValue(generator="system-uuid")
@GenericGenerator(name="system-uuid", strategy = "uuid")
public String getId() {
@Id @GeneratedValue(generator="trigger-generated")
@GenericGenerator(
name="trigger-generated",
86
Identifiers
strategy = "select",
parameters = @Parameter(name="key", value = "socialSecurityNumber")
)
public String getId() {
The hbm.xml approach uses the optional <generator> child element inside <id>. If any
parameters are required to configure or initialize the generator instance, they are passed using
the <param> element.
increment
gera identificadores dos tipos long, short ou int que são únicos apenas quando nenhum
outro processo está inserindo dados na mesma tabela. Não utilize em ambientes de cluster.
identity
suporta colunas de identidade em DB2, MySQL, Servidor MS SQL, Sybase e HypersonicSQL.
O identificador retornado é do tipo long, short ou int.
sequence
utiliza uma seqüência em DB2, PostgreSQL, Oracle, SAP DB, McKoi ou um gerador no
Interbase. O identificador de retorno é do tipo long, short ou int.
hilo
utiliza um algoritmo hi/lo para gerar de forma eficiente identificadores do tipo long, short
ou int, a partir de uma tabela e coluna fornecida (por padrão hibernate_unique_key e
next_hi) como fonte para os valores hi. O algoritmo hi/lo gera identificadores que são únicos
apenas para um banco de dados específico.
seqhilo
utiliza um algoritmo hi/lo para gerar de forma eficiente identificadores do tipo long, short ou
int, a partir de uma seqüência de banco de dados fornecida.
uuid
Generates a 128-bit UUID based on a custom algorithm. The value generated is
represented as a string of 32 hexidecimal digits. Users can also configure it to use
87
Capítulo 5. Mapeamento O/R Básico
a separator (config parameter "separator") which separates the hexidecimal digits into
8{sep}8{sep}4{sep}8{sep}4. Note specifically that this is different than the IETF RFC 4122
representation of 8-4-4-4-12. If you need RFC 4122 compliant UUIDs, consider using "uuid2"
generator discussed below.
uuid2
Generates a IETF RFC 4122 compliant (variant 2) 128-bit UUID. The exact "version" (the
RFC term) generated depends on the pluggable "generation strategy" used (see below).
Capable of generating values as java.util.UUID, java.lang.String or as a byte
array of length 16 (byte[16]). The "generation strategy" is defined by the interface
org.hibernate.id.UUIDGenerationStrategy. The generator defines 2 configuration
parameters for defining which generation strategy to use:
uuid_gen_strategy_class
Names the UUIDGenerationStrategy class to use
uuid_gen_strategy
Names the UUIDGenerationStrategy instance to use
guid
utiliza um string GUID gerado pelo banco de dados no Servidor MS SQL e MySQL.
native
seleciona entre identity, sequenceou hilo dependendo das capacidades do banco de
dados utilizado.
assigned
deixa a aplicação definir um identificador para o objeto antes que o save() seja chamado.
Esta é a estratégia padrão caso nenhum elemento <generator> seja especificado.
select
retorna a chave primária recuperada por um trigger do banco de dados, selecionando uma
linha pela chave única e recuperando o valor da chave primária.
foreign
utiliza o identificador de um outro objeto associado. Normalmente utilizado em conjunto com
uma associação de chave primária do tipo <one-to-one>.
88
Identifiers
sequence-identity
uma estratégia de geração de seqüência especializada que use uma seqüência de banco
de dados para a geração de valor atual, mas combina isto com JDBC3 getGeneratedKeys
para de fato retornar o valor do identificador gerado como parte da execução de instrução de
inserção. Esta estratégia é somente conhecida para suportar drivers da Oracle 10g, focados
em JDK 1.4. Note que os comentários sobre estas instruções de inserção estão desabilitados
devido a um bug nos drivers da Oracle.
Infelizmente, você não pode utilizar hilo quando estiver fornecendo sua própria Connection
para o Hibernate. Quando o Hibernate estiver usando uma fonte de dados do servidor de
aplicações para obter conexões suportadas com JTA, você precisará configurar adequadamente
o hibernate.transaction.manager_lookup_class.
O UUID contém: o endereço IP, hora de início da JVM que é com precisão de um quarto de
segundo, a hora do sistema e um valor contador que é único dentro da JVM. Não é possível
obter o endereço MAC ou um endereço de memória do código Java, portanto este é o melhor
que pode ser feito sem utilizar JNI.
Para bancos de dados que suportam colunas de identidade (DB2, MySQL, Sybase, MS SQL),
você pode utilizar uma geração de chave identity. Para bancos de dados que suportam
89
Capítulo 5. Mapeamento O/R Básico
sequencias (DB2, Oracle, PostgreSQL, Interbase, McKoi, SAP DB) você pode utilizar a geração
de chaves no estilo sequence. As duas estratégias requerem duas consultas SQL para inserir
um novo objeto.
If you want the application to assign identifiers, as opposed to having Hibernate generate them,
you can use the assigned generator. This special generator uses the identifier value already
assigned to the object's identifier property. The generator is used when the primary key is a natural
key instead of a surrogate key. This is the default behavior if you do not specify @GeneratedValue
nor <generator> elements.
O Hibernate não gera DDL com triggers, apenas para sistemas legados.
90
Identifiers
Finally, you can ask Hibernate to copy the identifier from another associated entity. In the
Hibernate jargon, it is known as a foreign generator but the JPA mapping reads better and is
encouraged.
@Entity
class MedicalHistory implements Serializable {
@Id @OneToOne
@JoinColumn(name = "person_id")
Person patient;
}
@Entity
public class Person implements Serializable {
@Id @GeneratedValue Integer id;
}
Or alternatively
@Entity
class MedicalHistory implements Serializable {
@Id Integer id;
@MapsId @OneToOne
@JoinColumn(name = "patient_id")
Person patient;
}
@Entity
class Person {
@Id @GeneratedValue Integer id;
}
<class name="MedicalHistory">
<id name="id">
<generator class="foreign">
<param name="property">patient</param>
</generator>
</id>
<one-to-one name="patient" class="Person" constrained="true"/>
</class>
Iniciando com a liberação 3.2.3, existem dois novos geradores que representam uma reavaliação
de dois diferentes aspectos da geração identificadora. O primeiro aspecto é a portabilidade
91
Capítulo 5. Mapeamento O/R Básico
do banco de dados, o segundo é a otimização. A otimização significa que você não precisa
questionar o banco de dados a cada solicitação para um novo valor de identificador. Estes dois
geradores possuem por intenção substituir alguns dos geradores nomeados acima, começando
em 3.3.x. No entanto, eles estão incluídos nas liberações atuais e podem ser referenciados pelo
FQN.
92
Identifiers
For identifier generators that store values in the database, it is inefficient for them to hit the
database on each and every call to generate a new identifier value. Instead, you can group a bunch
of them in memory and only hit the database when you have exhausted your in-memory value
group. This is the role of the pluggable optimizers. Currently only the two enhanced generators
(Seção 5.1.2.3, “Aprimoração dos geradores de identificador” support this operation.
• none (geralmente este é o padrão, caso nenhum otimizador for especificado): isto não
executará quaisquer otimizações e alcançará o banco de dados para cada e toda solicitação.
• hilo: aplica-se ao algoritmo em volta dos valores restaurados do banco de dados. Espera-se
que os valores a partir do banco de dados para este otimizador sejam seqüenciais. Os valores
restaurados a partir da estrutura do banco de dados para este otimizador indica um "número
de grupo". O increment_size é multiplicado pelo valor em memória para definir um grupo "hi
value".
• pooled: assim como o caso do hilo, este otimizador tenta minimizar o número de tentativas no
banco de dados. No entanto, nós simplesmente implementamos o valor de inicialização para o
"próximo grupo" na estrutura do banco de dados ao invés do valor seqüencial na combinação
com um algoritmo de agrupamento em memória. Neste caso, o increment_size refere-se aos
valores de entrada a partir do banco de dados.
Hibernate supports the automatic generation of some of the identifier properties. Simply use the
@GeneratedValue annotation on one or several id properties.
Atenção
The Hibernate team has always felt such a construct as fundamentally wrong. Try
hard to fix your data model before using this feature.
93
Capítulo 5. Mapeamento O/R Básico
@Entity
public class CustomerInventory implements Serializable {
@Id
@TableGenerator(name = "inventory",
table = "U_SEQUENCES",
pkColumnName = "S_ID",
valueColumnName = "S_NEXTNUM",
pkColumnValue = "inventory",
allocationSize = 1000)
@GeneratedValue(strategy = GenerationType.TABLE, generator = "inventory")
Integer id;
@Entity
public class Customer implements Serializable {
@Id
private int id;
}
When using long transactions or conversations that span several database transactions, it is useful
to store versioning data to ensure that if the same entity is updated by two conversations, the last
to commit changes will be informed and not override the other conversation's work. It guarantees
some isolation while still allowing for good scalability and works particularly well in read-often
write-sometimes situations.
A versão ou timestamp de uma propriedade nunca deve ser nula para uma instância
desconectada, assim o Hibernate irá identificar qualquer instância com uma versão nula ou
timestamp como transiente, não importando qual outra estratégia unsaved-value tenha sido
especificada. A declaração de uma versão nula ou a propriedade timestamp é um caminho
fácil para tratar problemas com reconexões transitivas no Hibernate, especialmente úteis para
pessoas utilizando identificadores atribuídos ou chaves compostas.
You can add optimistic locking capability to an entity using the @Version annotation:
@Entity
public class Flight implements Serializable {
...
@Version
94
Optimistic locking properties (optional)
@Column(name="OPTLOCK")
public Integer getVersion() { ... }
}
The version property will be mapped to the OPTLOCK column, and the entity manager will use it to
detect conflicting updates (preventing lost updates you might otherwise see with the last-commit-
wins strategy).
The version column may be a numeric. Hibernate supports any kind of type provided that you
define and implement the appropriate UserVersionType.
The application must not alter the version number set up by Hibernate in
any way. To artificially increase the version number, check in Hibernate Entity
Manager's reference documentation LockModeType.OPTIMISTIC_FORCE_INCREMENT or
LockModeType.PESSIMISTIC_FORCE_INCREMENT.
If the version number is generated by the database (via a trigger for example), make sure to use
@org.hibernate.annotations.Generated(GenerationTime.ALWAYS).
<version
column="version_column"
name="propertyName"
type="typename"
access="field|property|ClassName"
unsaved-value="null|negative|undefined"
generated="never|always"
insert="true|false"
node="element-name|@attribute-name|element/@attribute|."
/>
column (opcional - tem como padrão o nome da propriedade name): O nome da coluna
mantendo o número da versão.
name: O nome da propriedade da classe persistente.
type (opcional - padrão para integer): O tipo do número da versão.
access (opcional - padrão para property): A estratégia que o Hiberante deve utilizar para
acessar o valor da propriedade.
unsaved-value (opcional – valor padrão para undefined ): Um valor para a propriedade
versão que indica que uma instância foi instanciada recentemente (unsaved), distinguindo
de instâncias desconectadas que foram salvas ou carregadas em sessões anteriores.
(undefined especifica que o valor da propriedade de identificação deve ser utilizado).
generated (opcional - valor padrão never): Especifica que este valor de propriedade da
versão é na verdade gerado pelo banco de dados. Veja o generated properties para maiores
informações.
95
Capítulo 5. Mapeamento O/R Básico
insert (opcional - padrão para true): Especifica se a coluna de versão deve ser incluída
na instrução de inserção do SQL. Pode ser configurado como false se a coluna do banco
de dados estiver definida com um valor padrão de 0.
5.1.3.2. Timestamp
Alternatively, you can use a timestamp. Timestamps are a less safe implementation of optimistic
locking. However, sometimes an application might use the timestamps in other ways as well.
@Entity
public class Flight implements Serializable {
...
@Version
public Date getLastUpdate() { ... }
}
When using timestamp versioning you can tell Hibernate where to retrieve
the timestamp value from - database or JVM - by optionally adding the
@org.hibernate.annotations.Source annotation to the property. Possible values for the
value attribute of the annotation are org.hibernate.annotations.SourceType.VM and
org.hibernate.annotations.SourceType.DB. The default is SourceType.DB which is also
used in case there is no @Source annotation at all.
<timestamp
column="timestamp_column"
name="propertyName"
access="field|property|ClassName"
unsaved-value="null|undefined"
source="vm|db"
generated="never|always"
node="element-name|@attribute-name|element/@attribute|."
/>
column (opcional - padrão para o nome da propriedade): O nome da coluna que mantém
o timestamp.
name: O nome da propriedade no estilo JavaBeans do tipo Date ou Timestamp da classe
persistente.
96
Propriedade
access (opcional - padrão para property): A estratégia que o Hiberante deve utilizar para
acessar o valor da propriedade.
unsaved-value (opcional - padrão para null): Um valor de propriedade da versão que indica
que uma instância foi recentemente instanciada (unsaved), distinguindo-a de instâncias
desconectadas que foram salvas ou carregadas em sessões prévias. Undefined especifica
que um valor de propriedade de identificação deve ser utilizado.
source (opcional - padrão para vm): De onde o Hibernate deve recuperar o valor timestamp?
Do banco de dados ou da JVM atual? Timestamps baseados em banco de dados levam
a um overhead porque o Hibernate precisa acessar o banco de dados para determinar o
"próximo valor", mas é mais seguro para uso em ambientes de cluster. Observe também,
que nem todos os Dialects suportam a recuperação do carimbo de data e hora atual do
banco de dados, enquanto outros podem não ser seguros para utilização em bloqueios, pela
falta de precisão (Oracle 8, por exemplo).
generated (opcional - padrão para never): Especifica que o valor da propriedade timestamp
é gerado pelo banco de dados. Veja a discussão do generated properties para maiores
informações.
Nota
5.1.4. Propriedade
You need to decide which property needs to be made persistent in a given entity. This differs
slightly between the annotation driven metadata and the hbm.xml files.
In the annotations world, every non static non transient property (field or method depending on
the access type) of an entity is considered persistent, unless you annotate it as @Transient. Not
having an annotation for your property is equivalent to the appropriate @Basic annotation.
The @Basic annotation allows you to declare the fetching strategy for a property. If set to LAZY,
specifies that this property should be fetched lazily when the instance variable is first accessed. It
requires build-time bytecode instrumentation, if your classes are not instrumented, property level
lazy loading is silently ignored. The default is EAGER. You can also mark a property as not optional
thanks to the @Basic.optional attribute. This will ensure that the underlying column are not
nullable (if possible). Note that a better approach is to use the @NotNull annotation of the Bean
Validation specification.
97
Capítulo 5. Mapeamento O/R Básico
@Transient
String getLengthInMeter() { ... } //transient property
@Basic
int getLength() { ... } // persistent property
@Basic(fetch = FetchType.LAZY)
String getDetailedComment() { ... } // persistent property
@Temporal(TemporalType.TIME)
java.util.Date getDepartureTime() { ... } // persistent property
@Enumerated(EnumType.STRING)
Starred getNote() { ... } //enum persisted as String in database
counter, a transient field, and lengthInMeter, a method annotated as @Transient, and will be
ignored by the Hibernate. name, length, and firstname properties are mapped persistent and
eagerly fetched (the default for simple properties). The detailedComment property value will be
lazily fetched from the database once a lazy property of the entity is accessed for the first time.
Usually you don't need to lazy simple properties (not to be confused with lazy association fetching).
The recommended alternative is to use the projection capability of JP-QL (Java Persistence Query
Language) or Criteria queries.
JPA support property mapping of all basic types supported by Hibernate (all basic Java types ,
their respective wrappers and serializable classes). Hibernate Annotations supports out of the box
enum type mapping either into a ordinal column (saving the enum ordinal) or a string based column
(saving the enum string representation): the persistence representation, defaulted to ordinal, can
be overridden through the @Enumerated annotation as shown in the note property example.
In plain Java APIs, the temporal precision of time is not defined. When dealing with temporal
data you might want to describe the expected precision in database. Temporal data can have
DATE, TIME, or TIMESTAMP precision (ie the actual date, only the time, or both). Use the @Temporal
annotation to fine tune that.
@Lob indicates that the property should be persisted in a Blob or a Clob depending on the property
type: java.sql.Clob, Character[], char[] and java.lang.String will be persisted in a Clob.
java.sql.Blob, Byte[], byte[] and Serializable type will be persisted in a Blob.
@Lob
public String getFullText() {
return fullText;
}
@Lob
public byte[] getFullCode() {
return fullCode;
98
Propriedade
If the property type implements java.io.Serializable and is not a basic type, and if the property
is not annotated with @Lob, then the Hibernate serializable type is used.
5.1.4.1.1. Type
You can also manually specify a type using the @org.hibernate.annotations.Type and some
parameters if needed. @Type.type could be:
If you do not specify a type, Hibernate will use reflection upon the named property and guess
the correct Hibernate type. Hibernate will attempt to interpret the name of the return class of the
property getter using, in order, rules 2, 3, and 4.
Nota
@TypeDef(
name = "phoneNumber",
defaultForType = PhoneNumber.class,
typeClass = PhoneNumberType.class
)
@Entity
public class ContactDetails {
[...]
private PhoneNumber localPhoneNumber;
@Type(type="phoneNumber")
99
Capítulo 5. Mapeamento O/R Básico
The following example shows the usage of the parameters attribute to customize the TypeDef.
//in org/hibernate/test/annotations/entity/package-info.java
@TypeDefs(
{
@TypeDef(
name="caster",
typeClass = CasterStringType.class,
parameters = {
@Parameter(name="cast", value="lower")
}
)
}
)
package org.hibernate.test.annotations.entity;
//in org/hibernate/test/annotations/entity/Forest.java
public class Forest {
@Type(type="caster")
public String getSmallText() {
...
}
When using composite user type, you will have to express column definitions. The @Columns has
been introduced for that purpose.
@Type(type="org.hibernate.test.annotations.entity.MonetaryAmountUserType")
@Columns(columns = {
@Column(name="r_amount"),
@Column(name="r_currency")
})
public MonetaryAmount getAmount() {
return amount;
}
By default the access type of a class hierarchy is defined by the position of the @Id or @EmbeddedId
annotations. If these annotations are on a field, then only fields are considered for persistence
and the state is accessed via the field. If there annotations are on a getter, then only the getters
100
Propriedade
are considered for persistence and the state is accessed via the getter/setter. That works well in
practice and is the recommended approach.
Nota
The best use case is an embeddable class used by several entities that might not use the same
access type. In this case it is better to force the access type at the embeddable class level.
To force the access type on a given class, use the @Access annotation as showed below:
@Entity
public class Order {
@Id private Long id;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
@Entity
public class User {
private Long id;
@Id public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
@Embeddable
@Access(AcessType.PROPERTY)
public class Address {
private String street1;
public String getStreet1() { return street1; }
public void setStreet1() { this.street1 = street1; }
101
Capítulo 5. Mapeamento O/R Básico
You can also override the access type of a single property while keeping the other properties
standard.
@Entity
public class Order {
@Id private Long id;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
@Transient private String userId;
@Transient private String orderId;
@Access(AccessType.PROPERTY)
public String getOrderNumber() { return userId + ":" + orderId; }
public void setOrderNumber() { this.userId = ...; this.orderId = ...; }
}
In this example, the default access type is FIELD except for the orderNumber property. Note that
the corresponding field, if any must be marked as @Transient or transient.
@org.hibernate.annotations.AccessType
The annotation @org.hibernate.annotations.AccessType should be
considered deprecated for FIELD and PROPERTY access. It is still useful however
if you need to use a custom access type.
It is sometimes useful to avoid increasing the version number even if a given property is
dirty (particularly collections). You can do that by annotating the property (or collection) with
@OptimisticLock(excluded=true).
More formally, specifies that updates to this property do not require acquisition of the optimistic
lock.
The column(s) used for a property mapping can be defined using the @Column annotation. Use
it to override default values (see the JPA specification for more information on the defaults). You
can use this annotation at the property level for properties that are:
102
Propriedade
@Entity
public class Flight implements Serializable {
...
@Column(updatable = false, name = "flight_name", nullable = false, length=50)
public String getName() { ... }
The name property is mapped to the flight_name column, which is not nullable, has a length of
50 and is not updatable (making the property immutable).
This annotation can be applied to regular properties as well as @Id or @Version properties.
@Column(
name="columnName";
103
Capítulo 5. Mapeamento O/R Básico
5.1.4.1.5. Formula
Sometimes, you want the Database to do some computation for you rather than in the JVM, you
might also create some kind of virtual column. You can use a SQL fragment (aka formula) instead
of mapping a property into a column. This kind of property is read only (its value is calculated by
your formula fragment).
The SQL fragment can be as complex as you want and even include subselects.
<property
name="propertyName"
column="column_name"
type="typename"
update="true|false"
insert="true|false"
access="field|property|ClassName"
lazy="true|false"
unique="true|false"
not-null="true|false"
optimistic-lock="true|false"
generated="never|insert|always"
node="element-name|@attribute-name|element/@attribute|."
104
Propriedade
index="index_name"
unique_key="unique_key_id"
length="L"
precision="P"
scale="S"
/>
Se você não especificar um tipo, o Hibernate irá utilizar reflexão sobre a propriedade nomeada
para ter uma idéia do tipo de Hibernate correto. O Hibernate tentará interpretar o nome da classe
retornada, usando as regras 2, 3 e 4 nesta ordem. Em certos casos, você ainda precisará do
105
Capítulo 5. Mapeamento O/R Básico
A função access permite que você controle como o Hibernate irá acessar a propriedade
em tempo de execução. Por padrão, o Hibernate irá chamar os métodos get/set da
propriedades. Se você especificar access="field", o Hibernate irá bipassar os metodos get/
set, acessando o campo diretamente, usando reflexão. Você pode especificar sua própria
estratégia para acesso da propriedade criando uma classe que implemente a interface
org.hibernate.property.PropertyAccessor.
<property name="totalPrice"
formula="( SELECT SUM (li.quantity*p.price) FROM LineItem li, Product p
WHERE li.productId = p.productId
AND li.customerId = customerId
AND li.orderNumber = orderNumber )"/>
Observe que você pode referenciar as entidades da própria tabela, através da não declaração de
um alias para uma coluna particular. Isto seria o customerId no exemplo dado. Observe também
que você pode usar o mapeamento de elemento aninhado <formula>, se você não gostar de
usar o atributo.
It is possible to declare an embedded component inside an entity and even override its column
mapping. Component classes have to be annotated at the class level with the @Embeddable
annotation. It is possible to override the column mapping of an embedded object for a particular
entity using the @Embedded and @AttributeOverride annotation in the associated property:
@Entity
public class Person implements Serializable {
@Embedded
@AttributeOverrides( {
@AttributeOverride(name="iso2", column = @Column(name="bornIso2") ),
@AttributeOverride(name="name", column = @Column(name="bornCountryName") )
} )
106
Embedded objects (aka components)
Country bornIn;
...
}
@Embeddable
public class Address implements Serializable {
String city;
Country nationality; //no overriding here
}
@Embeddable
public class Country implements Serializable {
private String iso2;
@Column(name="countryName") private String name;
An embeddable object inherits the access type of its owning entity (note that you can override
that using the @Access annotation).
The Person entity has two component properties, homeAddress and bornIn. homeAddress
property has not been annotated, but Hibernate will guess that it is a persistent component by
looking for the @Embeddable annotation in the Address class. We also override the mapping of a
column name (to bornCountryName) with the @Embedded and @AttributeOverride annotations
for each mapped attribute of Country. As you can see, Country is also a nested component
of Address, again using auto-detection by Hibernate and JPA defaults. Overriding columns of
embedded objects of embedded objects is through dotted expressions.
@Embedded
@AttributeOverrides( {
@AttributeOverride(name="city", column = @Column(name="fld_city") ),
@AttributeOverride(name="nationality.iso2", column = @Column(name="nat_Iso2") ),
@AttributeOverride(name="nationality.name", column = @Column(name="nat_CountryName") )
//nationality columns in homeAddress are overridden
} )
Address homeAddress;
Hibernate Annotations supports something that is not explicitly supported by the JPA specification.
You can annotate a embedded object with the @MappedSuperclass annotation to make the
superclass properties persistent (see @MappedSuperclass for more informations).
107
Capítulo 5. Mapeamento O/R Básico
You can also use association annotations in an embeddable object (ie @OneToOne,
@ManyToOne, @OneToMany or @ManyToMany). To override the association columns you can use
@AssociationOverride.
If you want to have the same embeddable object type twice in the same entity, the column name
defaulting will not work as several embedded objects would share the same set of columns. In
plain JPA, you need to override at least one set of columns. Hibernate, however, allows you to
enhance the default naming mechanism through the NamingStrategy interface. You can write a
strategy that prevent name clashing in such a situation. DefaultComponentSafeNamingStrategy
is an example of this.
If a property of the embedded object points back to the owning entity, annotate it with the @Parent
annotation. Hibernate will make sure this property is properly loaded with the entity reference.
<component
name="propertyName"
class="className"
insert="true|false"
update="true|false"
access="field|property|ClassName"
lazy="true|false"
optimistic-lock="true|false"
unique="true|false"
node="element-name|."
>
<property ...../>
<many-to-one .... />
........
</component>
108
Inheritance strategy
optimistic-lock (opcional – padrão para true): Especifica que atualizações para este
componente requerem ou não aquisição de um bloqueio otimista. Em outras palavras,
determina se uma versão de incremento deve ocorrer quando esta propriedade estiver suja.
unique (opcional – valor padrão false): Especifica que existe uma unique restrição em
todas as colunas mapeadas do componente.
A tag filha <property> acrescenta a propriedade de mapeamento da classe filha para colunas
de uma tabela.
• Single table per class hierarchy strategy: a single table hosts all the instances of a class
hierarchy
• Joined subclass strategy: one table per class and subclass is present and each table persist the
properties specific to a given subclass. The state of the entity is then stored in its corresponding
class table and all its superclasses
• Table per class strategy: one table per concrete class and subclass is present and each table
persist the properties of the class and its superclasses. The state of the entity is then stored
entirely in the dedicated table for its class.
Each subclass declares its own persistent properties and subclasses. Version and id properties
are assumed to be inherited from the root class. Each subclass in a hierarchy must define a unique
discriminator value. If this is not specified, the fully qualified Java class name is used.
@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(
name="planetype",
discriminatorType=DiscriminatorType.STRING
)
@DiscriminatorValue("Plane")
public class Plane { ... }
109
Capítulo 5. Mapeamento O/R Básico
@Entity
@DiscriminatorValue("A320")
public class A320 extends Plane { ... }
<subclass
name="ClassName"
discriminator-value="discriminator_value"
proxy="ProxyInterface"
lazy="true|false"
dynamic-update="true|false"
dynamic-insert="true|false"
entity-name="EntityName"
node="element-name"
extends="SuperclassName">
For information about inheritance mappings see Capítulo 10, Mapeamento de Herança .
5.1.6.1.1. Discriminador
Use the @DiscriminatorColumn to define the discriminator column as well as the discriminator
type.
Nota
110
Inheritance strategy
CHAR and INTEGER which means that not all Hibernate supported types are
available via the @DiscriminatorColumn annotation.
You can also use @DiscriminatorFormula to express in SQL a virtual discriminator column. This
is particularly useful when the discriminator value can be extracted from one or more columns of
the table. Both @DiscriminatorColumn and @DiscriminatorFormula are to be set on the root
entity (once per persisted hierarchy).
Dica
There is also a @org.hibernate.annotations.ForceDiscriminator annotation
which is deprecated since version 3.6. Use @DiscriminatorOptions instead.
Finally, use @DiscriminatorValue on each class of the hierarchy to specify the value stored in
the discriminator column for a given entity. If you do not set @DiscriminatorValue on a class,
the fully qualified class name is used.
@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(
name="planetype",
discriminatorType=DiscriminatorType.STRING
)
@DiscriminatorValue("Plane")
public class Plane { ... }
@Entity
@DiscriminatorValue("A320")
public class A320 extends Plane { ... }
In hbm.xml, the <discriminator> element is used to define the discriminator column or formula:
<discriminator
column="discriminator_column"
type="discriminator_type"
111
Capítulo 5. Mapeamento O/R Básico
force="true|false"
insert="true|false"
Valores atuais de uma coluna discriminada são especificados pela função discriminator-value
da <class> e elementos da <subclass>.
Usando o atributo formula você pode declarar uma expressão SQL arbitrária que será utilizada
para avaliar o tipo de uma linha. Por exemplo:
<discriminator
formula="case when CLASS_TYPE in ('a', 'b', 'c') then 0 else 1 end"
type="integer"/>
@Entity @Table(name="CATS")
@Inheritance(strategy=InheritanceType.JOINED)
public class Cat implements Serializable {
@Id @GeneratedValue(generator="cat-uuid")
@GenericGenerator(name="cat-uuid", strategy="uuid")
String getId() { return id; }
...
}
@Entity @Table(name="DOMESTIC_CATS")
@PrimaryKeyJoinColumn(name="CAT")
public class DomesticCat extends Cat {
public String getName() { return name; }
112
Inheritance strategy
Nota
The table name still defaults to the non qualified class name. Also if
@PrimaryKeyJoinColumn is not set, the primary key / foreign key columns are
assumed to have the same names as the primary key columns of the primary table
of the superclass.
<joined-subclass
name="ClassName"
table="tablename"
proxy="ProxyInterface"
lazy="true|false"
dynamic-update="true|false"
dynamic-insert="true|false"
schema="schema"
catalog="catalog"
extends="SuperclassName"
persister="ClassName"
subselect="SQL expression"
entity-name="EntityName"
node="element-name">
Use the <key> element to declare the primary key / foreign key column. The mapping at the start
of the chapter would then be re-written as:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
113
Capítulo 5. Mapeamento O/R Básico
<hibernate-mapping package="eg">
<class name="eg.Dog">
<!-- mapping for Dog could go here -->
</class>
</hibernate-mapping>
For information about inheritance mappings see Capítulo 10, Mapeamento de Herança .
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Flight implements Serializable { ... }
Or in hbm.xml:
<union-subclass
name="ClassName"
table="tablename"
proxy="ProxyInterface"
lazy="true|false"
dynamic-update="true|false"
114
Inheritance strategy
dynamic-insert="true|false"
schema="schema"
catalog="catalog"
extends="SuperclassName"
abstract="true|false"
persister="ClassName"
subselect="SQL expression"
entity-name="EntityName"
node="element-name">
For information about inheritance mappings see Capítulo 10, Mapeamento de Herança .
This is sometimes useful to share common properties through a technical or a business superclass
without including it as a regular mapped entity (ie no specific table for this entity). For that purpose
you can map them as @MappedSuperclass.
@MappedSuperclass
public class BaseEntity {
@Basic
@Temporal(TemporalType.TIMESTAMP)
public Date getLastUpdate() { ... }
public String getLastUpdater() { ... }
...
}
In database, this hierarchy will be represented as an Order table having the id, lastUpdate and
lastUpdater columns. The embedded superclass property mappings are copied into their entity
subclasses. Remember that the embeddable superclass is not the root of the hierarchy though.
115
Capítulo 5. Mapeamento O/R Básico
Nota
Properties from superclasses not mapped as @MappedSuperclass are ignored.
Nota
The default access type (field or methods) is used, unless you use the @Access
annotation.
Nota
The same notion can be applied to @Embeddable objects to persist properties from
their superclasses. You also need to use @MappedSuperclass to do that (this
should not be considered as a standard EJB3 feature though)
Nota
It is allowed to mark a class as @MappedSuperclass in the middle of the mapped
inheritance hierarchy.
Nota
Any class in the hierarchy non annotated with @MappedSuperclass nor @Entity
will be ignored.
You can override columns defined in entity superclasses at the root entity level using the
@AttributeOverride annotation.
@MappedSuperclass
public class FlyingObject implements Serializable {
@Transient
public int getMetricAltitude() {
return metricAltitude;
}
@ManyToOne
public PropulsionType getPropulsion() {
116
Inheritance strategy
return metricAltitude;
}
...
}
@Entity
@AttributeOverride( name="altitude", column = @Column(name="fld_altitude") )
@AssociationOverride(
name="propulsion",
joinColumns = @JoinColumn(name="fld_propulsion_fk")
)
public class Plane extends FlyingObject {
...
}
The altitude property will be persisted in an fld_altitude column of table Plane and the
propulsion association will be materialized in a fld_propulsion_fk foreign key column.
In hbm.xml, simply map the properties of the superclass in the <class> element of the entity that
needs to inherit them.
While not recommended for a fresh schema, some legacy databases force your to map a single
entity on several tables.
@Entity
@Table(name="MainCat")
@SecondaryTables({
@SecondaryTable(name="Cat1", pkJoinColumns={
@PrimaryKeyJoinColumn(name="cat_id", referencedColumnName="id")
),
@SecondaryTable(name="Cat2", uniqueConstraints={@UniqueConstraint(columnNames={"storyPart2"})})
})
public class Cat implements Serializable {
@Id @GeneratedValue
public Integer getId() {
return id;
}
117
Capítulo 5. Mapeamento O/R Básico
@Column(table="Cat1")
public String getStoryPart1() {
return storyPart1;
}
@Column(table="Cat2")
public String getStoryPart2() {
return storyPart2;
}
}
In this example, name will be in MainCat. storyPart1 will be in Cat1 and storyPart2 will be in
Cat2. Cat1 will be joined to MainCat using the cat_id as a foreign key, and Cat2 using id (ie
the same column name, the MainCat id column has). Plus a unique constraint on storyPart2
has been set.
• fetch: If set to JOIN, the default, Hibernate will use an inner join to retrieve a secondary table
defined by a class or its superclasses and an outer join for a secondary table defined by a
subclass. If set to SELECT then Hibernate will use a sequential select for a secondary table
defined on a subclass, which will be issued only if a row turns out to represent an instance of
the subclass. Inner joins will still be used to retrieve a secondary defined by the class and its
superclasses.
• inverse: If true, Hibernate will not try to insert or update the properties defined by this join.
Default to false.
• optional: If enabled (the default), Hibernate will insert a row only if the properties defined by
this join are non-null and will always use an outer join to retrieve the properties.
• foreignKey: defines the Foreign Key name of a secondary table pointing back to the primary
table.
Make sure to use the secondary table name in the appliesto property
@Entity
@Table(name="MainCat")
@SecondaryTable(name="Cat1")
@org.hibernate.annotations.Table(
appliesTo="Cat1",
fetch=FetchMode.SELECT,
optional=true)
public class Cat implements Serializable {
118
Inheritance strategy
@Id @GeneratedValue
public Integer getId() {
return id;
}
@Column(table="Cat1")
public String getStoryPart1() {
return storyPart1;
}
@Column(table="Cat2")
public String getStoryPart2() {
return storyPart2;
}
}
<join
table="tablename"
schema="owner"
catalog="catalog"
fetch="join|select"
inverse="true|false"
optional="true|false">
119
Capítulo 5. Mapeamento O/R Básico
inverse (opcional – padrão para false): Se habilitado, o Hibernate não tentará inserir ou
atualizar as propriedades definidas por esta união.
optional (opcional – padrão para false): Se habilitado, o Hibernate irá inserir uma linha
apenas se as propriedades, definidas por esta junção, não forem nulas. Isto irá sempre usar
uma união externa para recuperar as propriedades.
Por exemplo, a informação de endereço para uma pessoa pode ser mapeada para uma tabela
separada, enquanto preservando o valor da semântica de tipos para todas as propriedades:
<class name="Person"
table="PERSON">
<join table="ADDRESS">
<key column="ADDRESS_ID"/>
<property name="address"/>
<property name="zip"/>
<property name="country"/>
</join>
...
Esta característica é útil apenas para modelos de dados legados. Nós recomendamos menos
tabelas do que classes e um modelo de domínio fine-grained. Porém, é útil para ficar trocando
entre estratégias de mapeamento de herança numa hierarquia simples, como explicaremos mais
a frente.
@ManyToOne and @OneToOne have a parameter named targetEntity which describes the target
entity name. You usually don't need this parameter since the default value (the type of the property
that stores the association) is good in almost all cases. However this is useful when you want to
use interfaces as the return type instead of the regular entity.
Setting a value of the cascade attribute to any meaningful value other than nothing will propagate
certain operations to the associated object. The meaningful values are divided into three
categories.
120
Mapping one to one and one to many associations
By default, single point associations are eagerly fetched in JPA 2. You can mark it as lazily
fetched by using @ManyToOne(fetch=FetchType.LAZY) in which case Hibernate will proxy the
association and load it when the state of the associated entity is reached. You can force Hibernate
not to use a proxy by using @LazyToOne(NO_PROXY). In this case, the property is fetched lazily
when the instance variable is first accessed. This requires build-time bytecode instrumentation.
lazy="false" specifies that the association will always be eagerly fetched.
With the default JPA options, single-ended associations are loaded with a subsequent select if
set to LAZY, or a SQL JOIN is used for EAGER associations. You can however adjust the fetching
strategy, ie how data is fetched by using @Fetch. FetchMode can be SELECT (a select is triggered
when the association needs to be loaded) or JOIN (use a SQL JOIN to load the association while
loading the owner entity). JOIN overrides any lazy attribute (an association loaded through a JOIN
strategy cannot be lazy).
• @OneToOne if only a single entity can point to the the target entity
and a foreign key in one table is referencing the primary key column(s) of the target table.
@Entity
public class Flight implements Serializable {
@ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} )
@JoinColumn(name="COMP_ID")
public Company getCompany() {
return company;
}
...
}
The @JoinColumn attribute is optional, the default value(s) is the concatenation of the name of
the relationship in the owner side, _ (underscore), and the name of the primary key column in the
owned side. In this example company_id because the property name is company and the column
id of Company is id.
@Entity
public class Flight implements Serializable {
@ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE}, targetEntity=CompanyImpl.class )
@JoinColumn(name="COMP_ID")
public Company getCompany() {
return company;
121
Capítulo 5. Mapeamento O/R Básico
}
...
}
You can also map a to one association through an association table. This association table
described by the @JoinTable annotation will contains a foreign key referencing back the entity
table (through @JoinTable.joinColumns) and a a foreign key referencing the target entity table
(through @JoinTable.inverseJoinColumns).
@Entity
public class Flight implements Serializable {
@ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} )
@JoinTable(name="Flight_Company",
joinColumns = @JoinColumn(name="FLIGHT_ID"),
inverseJoinColumns = @JoinColumn(name="COMP_ID")
)
public Company getCompany() {
return company;
}
...
}
Nota
You can use a SQL fragment to simulate a physical join column using the
@JoinColumnOrFormula / @JoinColumnOrformulas annotations (just like you can
use a SQL fragment to simulate a property column via the @Formula annotation).
@Entity
public class Ticket implements Serializable {
@ManyToOne
@JoinColumnOrFormula(formula="(firstname + ' ' + lastname)")
public Person getOwner() {
return person;
}
...
}
122
Mapping one to one and one to many associations
When Hibernate cannot resolve the association because the expected associated element is not in
database (wrong id on the association column), an exception is raised. This might be inconvenient
for legacy and badly maintained schemas. You can ask Hibernate to ignore such elements instead
of raising an exception using the @NotFound annotation.
@Entity
public class Child {
...
@ManyToOne
@NotFound(action=NotFoundAction.IGNORE)
public Parent getParent() { ... }
...
}
Sometimes you want to delegate to your database the deletion of cascade when a given entity is
deleted. In this case Hibernate generates a cascade delete constraint at the database level.
@Entity
public class Child {
...
@ManyToOne
@OnDelete(action=OnDeleteAction.CASCADE)
public Parent getParent() { ... }
...
}
Foreign key constraints, while generated by Hibernate, have a fairly unreadable name. You can
override the constraint name using @ForeignKey.
@Entity
public class Child {
...
@ManyToOne
@ForeignKey(name="FK_PARENT")
public Parent getParent() { ... }
...
}
alter table Child add constraint FK_PARENT foreign key (parent_id) references Parent
123
Capítulo 5. Mapeamento O/R Básico
Sometimes, you want to link one entity to an other not by the target entity primary key but
by a different unique key. You can achieve that by referencing the unique key column(s) in
@JoinColumn.referenceColumnName.
@Entity
class Person {
@Id Integer personNumber;
String firstName;
@Column(name="I")
String initial;
String lastName;
}
@Entity
class Home {
@ManyToOne
@JoinColumns({
@JoinColumn(name="first_name", referencedColumnName="firstName"),
@JoinColumn(name="init", referencedColumnName="I"),
@JoinColumn(name="last_name", referencedColumnName="lastName"),
})
Person owner
}
In hbm.xml, mapping an association is similar. The main difference is that a @OneToOne is mapped
as <many-to-one unique="true"/>, let's dive into the subject.
<many-to-one
name="propertyName"
column="column_name"
class="ClassName"
cascade="cascade_style"
fetch="join|select"
update="true|false"
insert="true|false"
property-ref="propertyNameFromAssociatedClass"
access="field|property|ClassName"
unique="true|false"
not-null="true|false"
optimistic-lock="true|false"
lazy="proxy|no-proxy|false"
not-found="ignore|exception"
entity-name="EntityName"
124
Mapping one to one and one to many associations
node="element-name|@attribute-name|element/@attribute|."
embed-xml="true|false"
index="index_name"
unique_key="unique_key_id"
foreign-key="foreign_key_name"
/>
125
Capítulo 5. Mapeamento O/R Básico
Setting a value of the cascade attribute to any meaningful value other than none will propagate
certain operations to the associated object. The meaningful values are divided into three
categories. First, basic operations, which include: persist, merge, delete, save-update,
evict, replicate, lock and refresh; second, special values: delete-orphan; and third,all
comma-separated combinations of operation names: cascade="persist,merge,evict" or
cascade="all,delete-orphan". See Seção 11.11, “Persistência Transitiva” for a full
explanation. Note that single valued, many-to-one and one-to-one, associations do not support
orphan delete.
O atributo property-ref deve apenas ser usado para mapear dados legados onde uma chave
exterior se refere à uma chave exclusiva da tabela associada que não seja a chave primária.
Este é um modelo relacional desagradável. Por exemplo, suponha que a classe Product tenha
um número seqüencial exclusivo, que não seja a chave primária. O atributo unique controla a
geração de DDL do Hibernate com a ferramenta SchemaExport.
The second approach is to ensure an entity and its associated entity share the same primary key.
In this case the primary key column is also a foreign key and there is no extra column. These
associations are always one to one.
126
Mapping one to one and one to many associations
@Entity
public class Body {
@Id
public Long getId() { return id; }
@OneToOne(cascade = CascadeType.ALL)
@MapsId
public Heart getHeart() {
return heart;
}
...
}
@Entity
public class Heart {
@Id
public Long getId() { ...}
}
Nota
Many people got confused by these primary key based one to one associations.
They can only be lazily loaded if Hibernate knows that the other side of the
association is always present. To indicate to Hibernate that it is the case, use
@OneToOne(optional=false).
<one-to-one
name="propertyName"
class="ClassName"
cascade="cascade_style"
constrained="true|false"
fetch="join|select"
property-ref="propertyNameFromAssociatedClass"
access="field|property|ClassName"
lazy="proxy|no-proxy|false"
entity-name="EntityName"
node="element-name|@attribute-name|element/@attribute|."
embed-xml="true|false"
foreign-key="foreign_key_name"
/>
127
Capítulo 5. Mapeamento O/R Básico
Associações de chave primária não necessitam de uma coluna extra de tabela. Se duas linhas
forem relacionadas pela associação, então as duas linhas da tabela dividem o mesmo valor da
chave primária. Assim, se você quiser que dois objetos sejam relacionados por uma associação
de chave primária, você deve ter certeza que foram atribuídos com o mesmo valor identificador.
128
Id Natural
Agora devemos assegurar que as chaves primárias de linhas relacionadas nas tabelas PERSON
e EMPLOYEE são iguais. Nós usamos uma estratégia especial de geração de identificador do
Hibernate chamada foreign:
Uma nova instância de Person é atribuída com o mesmo valor da chave primária da instância de
Employee referenciada com a propriedade employee daquela Person.
5.1.8. Id Natural
Although we recommend the use of surrogate keys as primary keys, you should try to identify
natural keys for all entities. A natural key is a property or combination of properties that is unique
and non-null. It is also immutable. Map the properties of the natural key as @NaturalId or map
them inside the <natural-id> element. Hibernate will generate the necessary unique key and
nullability constraints and, as a result, your mapping will be more self-documenting.
@Entity
public class Citizen {
@Id
@GeneratedValue
private Integer id;
private String firstname;
private String lastname;
@NaturalId
@ManyToOne
private State state;
@NaturalId
private String ssn;
...
}
129
Capítulo 5. Mapeamento O/R Básico
Or in XML,
<natural-id mutable="true|false"/>
<property ... />
<many-to-one ... />
......
</natural-id>
Nós recomendamos com ênfase que você implemente equals() e hashCode() para comparar
as propriedades da chave natural da entidade.
Este mapeamento não pretende ser utilizado com entidades com chaves naturais primárias.
• mutable (opcional, padrão false): Por padrão, propriedades naturais identificadoras são
consideradas imutáveis (constante).
5.1.9. Any
There is one more type of property mapping. The @Any mapping defines a polymorphic association
to classes from multiple tables. This type of mapping requires more than one column. The first
column contains the type of the associated entity. The remaining columns contain the identifier. It
is impossible to specify a foreign key constraint for this kind of association. This is not the usual
way of mapping polymorphic associations and you should use this only in special cases. For
example, for audit logs, user session data, etc.
The @Any annotation describes the column holding the metadata information. To link the value of
the metadata information and an actual entity type, The @AnyDef and @AnyDefs annotations are
used. The metaType attribute allows the application to specify a custom type that maps database
column values to persistent classes that have identifier properties of the type specified by idType.
You must specify the mapping from values of the metaType to class names.
Note that @AnyDef can be mutualized and reused. It is recommended to place it as a package
metadata in this case.
130
Any
//on a package
@AnyMetaDef( name="property"
idType = "integer",
metaType = "string",
metaValues = {
@MetaValue( value = "S", targetEntity = StringProperty.class ),
@MetaValue( value = "I", targetEntity = IntegerProperty.class )
} )
package org.hibernate.test.annotations.any;
//in a class
@Any( metaDef="property", metaColumn = @Column( name = "property_type" ), fetch=FetchType.EAGER )
@JoinColumn( name = "property_id" )
public Property getMainProperty() {
return mainProperty;
}
Nota
<any
name="propertyName"
id-type="idtypename"
meta-type="metatypename"
cascade="cascade_style"
access="field|property|ClassName"
optimistic-lock="true|false"
>
<meta-value ... />
<meta-value ... />
.....
<column .... />
<column .... />
.....
131
Capítulo 5. Mapeamento O/R Básico
</any>
5.1.10. Propriedades
O elemento <properties> permite a definição de um grupo com nome, lógico de propriedades
de uma classe. A função mais importante do construtor é que ele permite que a combinação
de propriedades seja o objetivo de uma property-ref. É também um modo conveninente para
definir uma restrição única de múltiplas colunas. Por exemplo:
<properties
name="logicalName"
insert="true|false"
update="true|false"
optimistic-lock="true|false"
unique="true|false"
>
<property ...../>
<many-to-one .... />
........
</properties>
132
Propriedades
<class name="Person">
<id name="personNumber"/>
...
<properties name="name"
unique="true" update="false">
<property name="firstName"/>
<property name="initial"/>
<property name="lastName"/>
</properties>
</class>
Então podemos ter uma associação de dados legados que referem a esta chave exclusiva da
tabela Person, ao invés de se referirem a chave primária:
<many-to-one name="owner"
class="Person" property-ref="name">
<column name="firstName"/>
<column name="initial"/>
<column name="lastName"/>
</many-to-one>
Nota
@Entity
class Person {
@Id Integer personNumber;
String firstName;
@Column(name="I")
String initial;
String lastName;
}
@Entity
class Home {
@ManyToOne
@JoinColumns({
@JoinColumn(name="first_name", referencedColumnName="firstName"),
@JoinColumn(name="init", referencedColumnName="I"),
@JoinColumn(name="last_name", referencedColumnName="lastName"),
})
Person owner
}
133
Capítulo 5. Mapeamento O/R Básico
Nós não recomendamos o uso deste tipo de coisa fora do contexto de mapeamento de dados
legados.
The hbm.xml structure has some specificities naturally not present when using annotations, let's
describe them briefly.
5.1.11.1. Doctype
Todos os mapeamentos de XML devem declarar o doctype exibido. O DTD atual pode ser
encontrado na URL abaixo, no diretório hibernate-x.x.x/src/org/ hibernate ou no
hibernate3.jar. O Hibernate sempre irá procurar pelo DTD inicialmente no seu classpath. Se
você tentar localizar o DTD usando uma conexão de internet, compare a declaração do seu DTD
com o conteúdo do seu classpath.
O Hibernate irá primeiro tentar solucionar os DTDs em seus classpath. Isto é feito, registrando
uma implementação org.xml.sax.EntityResolver personalizada com o SAXReader que ele
utiliza para ler os arquivos xml. Este EntityResolver personalizado, reconhece dois nomes de
espaço de sistemas Id diferentes:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" [
<!ENTITY types SYSTEM "classpath://your/domain/types.xml">
]>
<hibernate-mapping package="your.domain">
<class name="MyEntity">
<id name="id" type="my-custom-id-type">
...
</id>
<class>
&types;
134
Some hbm.xml specificities
</hibernate-mapping>
Este elemento possui diversos atributos opcionais. Os atributos schema e catalog especificam
que tabelas referenciadas neste mapeamento pertencem ao esquema e/ou ao catálogo nomeado.
Se especificados, os nomes das tabelas serão qualificados no esquema ou catálogo dado. Se
não, os nomes das tabelas não serão qualificados. O atributo default-cascade especifica qual
estilo de cascata será considerado pelas propriedades e coleções que não especificarem uma
função cascade. A função auto-import nos deixa utilizar nomes de classes não qualificados na
linguagem de consulta, por padrão.
<hibernate-mapping
schema="schemaName"
catalog="catalogName"
default-cascade="cascade_style"
default-access="field|property|ClassName"
default-lazy="true|false"
auto-import="true|false"
package="package.name"
/>
Se você tem duas classes persistentes com o mesmo nome (não qualificadas), você deve ajustar
auto-import="false". Caso você tentar ajustar duas classes para o mesmo nome "importado",
isto resultará numa exceção.
Observe que o elemento hibernate-mapping permite que você aninhe diversos mapeamentos
de <class> persistentes, como mostrado abaixo. Entretanto, é uma boa prática (e esperado
135
Capítulo 5. Mapeamento O/R Básico
por algumas ferramentas) o mapeamento de apenas uma classe persistente simples (ou
uma hierarquia de classes simples) em um arquivo de mapeamento e nomeá-la após a
superclasse persistente, por exemplo: Cat.hbm.xml, Dog.hbm.xml, ou se estiver usando
herança, Animal.hbm.xml.
5.1.11.3. Key
The <key> element is featured a few times within this guide. It appears anywhere the parent
mapping element defines a join to a new table that references the primary key of the original table.
It also defines the foreign key in the joined table:
<key
column="columnname"
on-delete="noaction|cascade"
property-ref="propertyName"
not-null="true|false"
update="true|false"
unique="true|false"
/>
column (opcional): O nome da coluna da chave exterior. Isto pode também ser especificado
através de elementos aninhados <column>.
on-delete (opcional, padrão para noaction): Especifica se a restrição da chave exterior no
banco de dados está habilitada para o deletar cascade.
property-ref (opcional): Especifica que a chave exterior se refere a colunas que não são
chave primária da tabela original. Útil para os dados legados.
not-null (opcional): Especifica que a coluna da chave exterior não aceita valores nulos. Isto
é implícito em qualquer momento que a chave exterior também fizer parte da chave primária.
update (opcional): Especifica que a chave exterior nunca deve ser atualizada. Isto está
implícito em qualquer momento que a chave exterior também fizer parte da chave primária.
unique (opcional): Especifica que a chave exterior deve ter uma restrição única. Isto é,
implícito em qualquer momento que a chave exterior também fizer parte da chave primária.
Nós recomendamos que para sistemas que o desempenho deletar seja importante, todas as
chaves devem ser definidas on-delete="cascade". O Hibernate irá usar uma restrição a nível
de banco de dados ON CASCADE DELETE, ao invés de muitas instruções DELETE. Esteja ciente
que esta característica é um atalho da estratégia usual de bloqueio otimista do Hibernate para
dados versionados.
As funções not-null e update são úteis quando estamos mapeando uma associação
unidirecional um para muitos. Se você mapear uma associação unidirecional um para muitos
para uma chave exterior não-nula, você deve declarar a coluna chave usando <key not-
null="true">.
136
Some hbm.xml specificities
5.1.11.4. Importar
Vamos supor que a sua aplicação tenha duas classes persistentes com o mesmo nome, e você
não quer especificar o nome qualificado do pacote nas consultas do Hibernate. As Classes
deverão ser "importadas" explicitamente, de preferência contando com auto-import="true".
Você pode até importar classes e interfaces que não estão explicitamente mapeadas:
<import
class="ClassName"
rename="ShortName"
/>
Nota
Qualquer elemento de mapeamento que aceita uma função column irá aceitar alternativamente
um sub-elemento <column>. Da mesma forma, <formula> é uma alternativa para a função
formula.
<column
name="column_name"
length="N"
precision="N"
scale="N"
not-null="true|false"
unique="true|false"
unique-key="multicolumn_unique_key_name"
index="index_name"
sql-type="sql_type_name"
check="SQL expression"
default="SQL expression"
read="SQL expression"
write="SQL expression"/>
137
Capítulo 5. Mapeamento O/R Básico
<formula>SQL expression</formula>
A maioria das funções no column fornecem um significado de junção do DDL durante a geração
automática do esquema. As funções read e write permitem que você especifique o SQL
personalizado, do qual o Hibernate usará para acessar o valor da coluna. Consulte a discussão
da column read and write expressions para maiores informações.
Os elementos column e formula podem até ser combinados dentro da mesma propriedade ou
associação mapeando para expressar, por exemplo, condições de associações exóticas.
Uma entidade existe independentemente de qualquer outro objeto guardando referências para
a entidade. Em contraste com o modelo usual de Java que um objeto não referenciado é
coletado pelo coletor de lixo. Entidades devem ser explicitamente salvas ou deletadas (exceto em
operações de salvamento ou deleção que possam ser executada em cascata de uma entidade pai
para seus filhos). Isto é diferente do modelo ODMG de persistência do objeto por acessibilidade
e se refere mais à forma como os objetos de aplicações são geralmente usados em grandes
sistemas. Entidades suportam referências circulares e comuns. Eles podem ser versionados.
Até agora, estivemos usando o termo "classe persistente" para referir às entidades.
Continuaremos a fazer isto. No entanto, nem todas as classes definidas pelo usuário com
estados persistentes são entidades. Um componente é uma classe de usuário definida com
valores semânticos. Uma propriedade de Java de tipo java.lang.String também tem um valor
semântico. Dada esta definição, nós podemos dizer que todos os tipos (classes) fornecidos pelo
JDK têm tipo de valor semântico em Java, enquanto que tipos definidos pelo usuário, podem ser
138
Valores de tipos básicos
mapeados com entidade ou valor de tipo semântico. Esta decisão pertence ao desenvolvedor da
aplicação. Uma boa dica para uma classe de entidade em um modelo de domínio são referências
comuns para uma instância simples daquela classe, enquanto a composição ou agregação
geralmente se traduz para um tipo de valor.
Todos os tipos internos do hibernate exceto coleções, suportam semânticas nulas com a exceção
das coleções.
integer, long, short, float, double, character, byte, boolean, yes_no, true_false
Tipos de mapeamentos de classes primitivas ou wrapper Java específicos (vendor-specific)
para tipos de coluna SQL. Boolean, boolean, yes_no são todas codificações alternativas
para um boolean ou java.lang.Boolean do Java.
string
Um tipo de mapeamento de java.lang.String para VARCHAR (ou VARCHAR2 no Oracle).
calendar, calendar_date
Tipo de mapeamento de java.util.Calendar para os tipos SQL TIMESTAMP e DATE (ou
equivalente).
big_decimal, big_integer
Tipo de mapeamento de java.math.BigDecimal and java.math.BigInteger para NUMERIC
(ou NUMBER no Oracle).
139
Capítulo 5. Mapeamento O/R Básico
class
Um tipo de mapeamento de java.lang.Class para VARCHAR (ou VARCHAR2 no Oracle). Uma
Class é mapeada pelo seu nome qualificado (completo).
binary
Mapeia matrizes de bytes para um tipo binário de SQL apropriado.
text
Mapeia strings de Java longos para um tipo SQL CLOB ou TEXT.
serializable
Mapeia tipos Java serializáveis para um tipo binário SQL apropriado. Você pode também
indicar o tipo serializable do Hibernate com o nome da classe ou interface Java serializável
que não é padrão para um tipo básico.
clob, blob
Tipos de mapeamentos para as classes JDBC java.sql.Clob and java.sql.Blob. Estes
tipos podem ser inconvenientes para algumas aplicações, visto que o objeto blob ou clob
não pode ser reusado fora de uma transação. Além disso, o suporte de driver é imcompleto
e inconsistente.
Identificadores únicos das entidades e coleções podem ser de qualquer tipo básico exceto
binary, blob ou clob. (Identificadores compostos também são permitidos. Leia abaixo para
maiores informações.
140
Tipos de valores personalizados
Observe o uso da tag <column> para mapear uma propriedade para colunas múltiplas.
Você mesmo pode fornecer parâmetros a um UserType no arquivo de mapeamento. Para isto,
seu UserType deve implementar a interface org.hibernate.usertype.ParameterizedType.
Para fornecer parâmetros a seu tipo personalizado, você pode usar o elemento <type> em seus
arquivos de mapeamento.
<property name="priority">
<type name="com.mycompany.usertypes.DefaultValueIntegerType">
<param name="default">0</param>
</type>
</property>
O UserType pode agora recuperar o valor para o parâmetro chamado padrão da Propriedade
do passado a ele.
Se você usar freqüentemente um determinado UserType, pode ser útil definir um nome mais
curto para ele. Você pode fazer isto usando o elemento <typedef>. Typedefs atribui um nome
a um tipo personalizado, e pode também conter uma lista de valores de parâmetro padrão se o
tipo for parametrizado.
141
Capítulo 5. Mapeamento O/R Básico
Note como as associações são agora especificadas utilizando o entity-name ao invés da class.
Nota
142
Propriedades geradas
never (padrão) - significa que o valor de propriedade dado não é gerado dentro do banco de
dados.
insert: informa que o valor de propriedade dado é gerado ao inserir, mas não é novamente
gerado nas próximas atualizações. Propriedades do tipo data criada, se encaixam nesta
categoria. Note que embora as propriedades version e timestamp podem ser marcadas como
geradas, esta opção não está disponível.
always - informa que o valor da propriedade é gerado tanto ao inserir quanto ao atualizar.
@Entity
class CreditCard {
@Column(name="credit_card_num")
@ColumnTransformer(
read="decrypt(credit_card_num)",
write="encrypt(?)")
public String getCreditCardNumber() { return creditCardNumber; }
public void setCreditCardNumber(String number) { this.creditCardNumber = number; }
private String creditCardNumber;
}
143
Capítulo 5. Mapeamento O/R Básico
or in XML
<property name="creditCardNumber">
<column
name="credit_card_num"
read="decrypt(credit_card_num)"
write="encrypt(?)"/>
</property>
Nota
You can use the plural form @ColumnTransformers if more than one columns need
to define either of these rules.
If a property uses more that one column, you must use the forColumn attribute to specify which
column, the expressions are targeting.
@Entity
class User {
@Type(type="com.acme.type.CreditCardType")
@Columns( {
@Column(name="credit_card_num"),
@Column(name="exp_date") } )
@ColumnTransformer(
forColumn="credit_card_num",
read="decrypt(credit_card_num)",
write="encrypt(?)")
public CreditCard getCreditCard() { return creditCard; }
public void setCreditCard(CreditCard card) { this.creditCard = card; }
private CreditCard creditCard;
}
• Esta propriedade é suportada por uma ou mais colunas que são exportadas como parte da
geração do esquema automático.
• Esta propriedade é de gravação-leitura, e não de leitura apenas.
144
Objetos de Banco de Dados Auxiliares
<hibernate-mapping>
...
<database-object>
<create>CREATE TRIGGER my_trigger ...</create>
<drop>DROP TRIGGER my_trigger</drop>
</database-object>
</hibernate-mapping>
O segundo módulo é para fornecer uma classe padrão que sabe como construir
os comandos CREATE e DROP. Esta classe padrão deve implementar a interface
org.hibernate.mapping.AuxiliaryDatabaseObject.
<hibernate-mapping>
...
<database-object>
<definition class="MyTriggerDefinition"/>
</database-object>
</hibernate-mapping>
Além disso, estes objetos de banco de dados podem ter um escopo opcional que só será aplicado
quando certos dialetos forem utilizados.
<hibernate-mapping>
...
<database-object>
<definition class="MyTriggerDefinition"/>
<dialect-scope name="org.hibernate.dialect.Oracle9iDialect"/>
<dialect-scope name="org.hibernate.dialect.Oracle10gDialect"/>
</database-object>
</hibernate-mapping>
Nota
145
146
Types
As an Object/Relational Mapping solution, Hibernate deals with both the Java and JDBC
representations of application data. An online catalog application, for example, most likely has
Product object with a number of attributes such as a sku, name, etc. For these individual
attributes, Hibernate must be able to read the values out of the database and write them
back. This 'marshalling' is the function of a Hibernate type, which is an implementation of the
org.hibernate.type.Type interface. In addition, a Hibernate type describes various aspects of
behavior of the Java type such as "how is equality checked?" or "how are values cloned?".
Importante
A Hibernate type is neither a Java type nor a SQL datatype; it provides a information
about both.
When you encounter the term type in regards to Hibernate be aware that usage
might refer to the Java type, the SQL/JDBC type or the Hibernate type.
Hibernate categorizes types into two high-level groups: value types (see Seção 6.1, “Value types”)
and entity types (see Seção 6.2, “Entity types”).
6.1.1.1. java.lang.String
org.hibernate.type.StringType
Maps a string to the JDBC VARCHAR type. This is the standard mapping for a string if no
Hibernate type is specified.
Registered under string and java.lang.String in the type registry (see Seção 6.5, “Type
registry”).
147
Capítulo 6. Types
org.hibernate.type.MaterializedClob
Maps a string to a JDBC CLOB type
Registered under materialized_clob in the type registry (see Seção 6.5, “Type registry”).
org.hibernate.type.TextType
Maps a string to a JDBC LONGVARCHAR type
Registered under text in the type registry (see Seção 6.5, “Type registry”).
org.hibernate.type.CharacterType
Maps a char or java.lang.Character to a JDBC CHAR
Registered under char and java.lang.Character in the type registry (see Seção 6.5, “Type
registry”).
org.hibernate.type.BooleanType
Maps a boolean to a JDBC BIT type
Registered under boolean and java.lang.Boolean in the type registry (see Seção 6.5, “Type
registry”).
org.hibernate.type.NumericBooleanType
Maps a boolean to a JDBC INTEGER type as 0 = false, 1 = true
Registered under numeric_boolean in the type registry (see Seção 6.5, “Type registry”).
org.hibernate.type.YesNoType
Maps a boolean to a JDBC CHAR type as ('N' | 'n') = false, ( 'Y' | 'y' ) = true
Registered under yes_no in the type registry (see Seção 6.5, “Type registry”).
org.hibernate.type.TrueFalseType
Maps a boolean to a JDBC CHAR type as ('F' | 'f') = false, ( 'T' | 't' ) = true
Registered under true_false in the type registry (see Seção 6.5, “Type registry”).
org.hibernate.type.ByteType
Maps a byte or java.lang.Byte to a JDBC TINYINT
Registered under byte and java.lang.Byte in the type registry (see Seção 6.5, “Type
registry”).
148
Basic value types
org.hibernate.type.ShortType
Maps a short or java.lang.Short to a JDBC SMALLINT
Registered under short and java.lang.Short in the type registry (see Seção 6.5, “Type
registry”).
org.hibernate.type.IntegerTypes
Maps an int or java.lang.Integer to a JDBC INTEGER
Registered under int and java.lang.Integerin the type registry (see Seção 6.5, “Type
registry”).
org.hibernate.type.LongType
Maps a long or java.lang.Long to a JDBC BIGINT
Registered under long and java.lang.Long in the type registry (see Seção 6.5, “Type
registry”).
org.hibernate.type.FloatType
Maps a float or java.lang.Float to a JDBC FLOAT
Registered under float and java.lang.Float in the type registry (see Seção 6.5, “Type
registry”).
org.hibernate.type.DoubleType
Maps a double or java.lang.Double to a JDBC DOUBLE
Registered under double and java.lang.Double in the type registry (see Seção 6.5, “Type
registry”).
6.1.1.10. java.math.BigInteger
org.hibernate.type.BigIntegerType
Maps a java.math.BigInteger to a JDBC NUMERIC
149
Capítulo 6. Types
6.1.1.11. java.math.BigDecimal
org.hibernate.type.BigDecimalType
Maps a java.math.BigDecimal to a JDBC NUMERIC
org.hibernate.type.TimestampType
Maps a java.sql.Timestamp to a JDBC TIMESTAMP
6.1.1.13. java.sql.Time
org.hibernate.type.TimeType
Maps a java.sql.Time to a JDBC TIME
Registered under time and java.sql.Time in the type registry (see Seção 6.5, “Type
registry”).
6.1.1.14. java.sql.Date
org.hibernate.type.DateType
Maps a java.sql.Date to a JDBC DATE
Registered under date and java.sql.Date in the type registry (see Seção 6.5, “Type
registry”).
6.1.1.15. java.util.Calendar
org.hibernate.type.CalendarType
Maps a java.util.Calendar to a JDBC TIMESTAMP
org.hibernate.type.CalendarDateType
Maps a java.util.Calendar to a JDBC DATE
Registered under calendar_date in the type registry (see Seção 6.5, “Type registry”).
6.1.1.16. java.util.Currency
org.hibernate.type.CurrencyType
Maps a java.util.Currency to a JDBC VARCHAR (using the Currency code)
150
Basic value types
Registered under currency and java.util.Currency in the type registry (see Seção 6.5,
“Type registry”).
6.1.1.17. java.util.Locale
org.hibernate.type.LocaleType
Maps a java.util.Locale to a JDBC VARCHAR (using the Locale code)
Registered under locale and java.util.Locale in the type registry (see Seção 6.5, “Type
registry”).
6.1.1.18. java.util.TimeZone
org.hibernate.type.TimeZoneType
Maps a java.util.TimeZone to a JDBC VARCHAR (using the TimeZone ID)
Registered under timezone and java.util.TimeZone in the type registry (see Seção 6.5,
“Type registry”).
6.1.1.19. java.net.URL
org.hibernate.type.UrlType
Maps a java.net.URL to a JDBC VARCHAR (using the external form)
Registered under url and java.net.URL in the type registry (see Seção 6.5, “Type registry”).
6.1.1.20. java.lang.Class
org.hibernate.type.ClassType
Maps a java.lang.Class to a JDBC VARCHAR (using the Class name)
Registered under class and java.lang.Class in the type registry (see Seção 6.5, “Type
registry”).
6.1.1.21. java.sql.Blob
org.hibernate.type.BlobType
Maps a java.sql.Blob to a JDBC BLOB
Registered under blob and java.sql.Blob in the type registry (see Seção 6.5, “Type
registry”).
6.1.1.22. java.sql.Clob
org.hibernate.type.ClobType
Maps a java.sql.Clob to a JDBC CLOB
151
Capítulo 6. Types
Registered under clob and java.sql.Clob in the type registry (see Seção 6.5, “Type
registry”).
6.1.1.23. byte[]
org.hibernate.type.BinaryType
Maps a primitive byte[] to a JDBC VARBINARY
Registered under binary and byte[] in the type registry (see Seção 6.5, “Type registry”).
org.hibernate.type.MaterializedBlobType
Maps a primitive byte[] to a JDBC BLOB
Registered under materialized_blob in the type registry (see Seção 6.5, “Type registry”).
6.1.1.24. byte[]
org.hibernate.type.BinaryType
Maps a java.lang.Byte[] to a JDBC VARBINARY
Registered under wrapper-binary, Byte[] and java.lang.Byte[] in the type registry (see
Seção 6.5, “Type registry”).
6.1.1.25. char[]
org.hibernate.type.CharArrayType
Maps a char[] to a JDBC VARCHAR
Registered under characters and char[] in the type registry (see Seção 6.5, “Type registry”).
6.1.1.26. char[]
org.hibernate.type.CharacterArrayType
Maps a java.lang.Character[] to a JDBC VARCHAR
6.1.1.27. java.util.UUID
org.hibernate.type.UUIDBinaryType
Maps a java.util.UUID to a JDBC BINARY
Registered under uuid-binary and java.util.UUID in the type registry (see Seção 6.5,
“Type registry”).
org.hibernate.type.UUIDCharType
Maps a java.util.UUID to a JDBC CHAR (though VARCHAR is fine too for existing schemas)
Registered under uuid-char in the type registry (see Seção 6.5, “Type registry”).
152
Composite types
org.hibernate.type.PostgresUUIDType
Maps a java.util.UUID to the PostgreSQL UUID data type (through Types#OTHER which is how
the PostgreSQL JDBC driver defines it).
Registered under pg-uuid in the type registry (see Seção 6.5, “Type registry”).
6.1.1.28. java.io.Serializable
org.hibernate.type.SerializableType
Maps implementors of java.lang.Serializable to a JDBC VARBINARY
Unlike the other value types, there are multiple instances of this type. It gets registered
once under java.io.Serializable. Additionally it gets registered under the specific
java.io.Serializable implementation class names.
Nota
The Java Persistence API calls these embedded types, while Hibernate
traditionally called them components. Just be aware that both terms are used and
mean the same thing in the scope of discussing Hibernate.
Components represent aggregations of values into a single Java type. For example, you might
have an Address class that aggregates street, city, state, etc information or a Name class that
aggregates the parts of a person's Name. In many ways a component looks exactly like an entity.
They are both (generally speaking) classes written specifically for the application. They both might
have references to other application-specific classes, as well as to collections and simple JDK
types. As discussed before, the only distinguishing factory is the fact that a component does not
own its own lifecycle nor does it define an identifier.
Importante
It is critical understand that we mean the collection itself, not its contents. The
contents of the collection can in turn be basic, component or entity types (though
not collections), but the collection itself is owned.
153
Capítulo 6. Types
correlate to rows in a table. Specifically they correlate to the row by means of a unique identifier.
Because of this unique identifier, entities exist independently and define their own lifecycle. As an
example, when we delete a Membership, both the User and Group entities remain.
Nota
The main categorization was between entity types and value types. To review we said that entities,
by nature of their unique identifier, exist independently of other objects whereas values do not. An
application cannot "delete" a Product sku; instead, the sku is removed when the Product itself is
deleted (obviously you can update the sku of that Product to null to make it "go away", but even
there the access is done through the Product).
Nor can you define an association to that Product sku. You can define an association to Product
based on its sku, assuming sku is unique, but that is totally different.
TBC...
154
Custom types using org.hibernate.type.Type
for the exposed property type. In our example, the property type would be Money, which is the key
we would use to register our type in the registry:
public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws SQLEx
assert names.length == 2;
BigDecimal amount = BigDecimalType.INSTANCE.get( names[0] ); // already handles null check
Currency currency = CurrencyType.INSTANCE.get( names[1] ); // already handles null check
return amount == null && currency == null
? null
: new Money( amount, currency );
}
public void nullSafeSet(PreparedStatement st, Object value, int index, boolean[] settable, SessionImplementor
throws SQLException {
if ( value == null ) {
BigDecimalType.INSTANCE.set( st, null, index );
CurrencyType.INSTANCE.set( st, null, index+1 );
}
else {
final Money money = (Money) value;
BigDecimalType.INSTANCE.set( st, money.getAmount(), index );
CurrencyType.INSTANCE.set( st, money.getCurrency(), index+1 );
}
}
...
}
155
Capítulo 6. Types
Importante
It is important that we registered the type before adding mappings.
Nota
Both org.hibernate.usertype.UserType and
org.hibernate.usertype.CompositeUserType were originally added to isolate
user code from internal changes to the org.hibernate.type.Type interfaces.
public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws SQLException {
assert names.length == 2;
BigDecimal amount = BigDecimalType.INSTANCE.get( names[0] ); // already handles null check
Currency currency = CurrencyType.INSTANCE.get( names[1] ); // already handles null check
return amount == null && currency == null
? null
: new Money( amount, currency );
}
public void nullSafeSet(PreparedStatement st, Object value, int index) throws SQLException {
if ( value == null ) {
BigDecimalType.INSTANCE.set( st, null, index );
CurrencyType.INSTANCE.set( st, null, index+1 );
}
else {
final Money money = (Money) value;
156
Custom types using org.hibernate.usertype.CompositeUserType
...
}
There is not much difference between the org.hibernate.type.Type example and the
org.hibernate.usertype.UserType example, but that is only because of the snippets shown.
If you choose the org.hibernate.type.Type approach there are quite a few more methods you
would need to implement as compared to the org.hibernate.usertype.UserType.
157
Capítulo 6. Types
public void setPropertyValue(Object component, int propertyIndex, Object value) throws HibernateException
if ( component == null ) {
return;
}
public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws SQLEx
assert names.length == 2;
BigDecimal amount = BigDecimalType.INSTANCE.get( names[0] ); // already handles null check
Currency currency = CurrencyType.INSTANCE.get( names[1] ); // already handles null check
return amount == null && currency == null
? null
: new Money( amount, currency );
}
public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws SQLE
if ( value == null ) {
BigDecimalType.INSTANCE.set( st, null, index );
CurrencyType.INSTANCE.set( st, null, index+1 );
}
else {
final Money money = (Money) value;
BigDecimalType.INSTANCE.set( st, money.getAmount(), index );
CurrencyType.INSTANCE.set( st, money.getCurrency(), index+1 );
}
}
...
}
158
Type registry
To register a new type or to override an existing type registration, applications would make
use of the registerTypeOverride method of the org.hibernate.cfg.Configuration class
when bootstrapping Hibernate. For example, lets say you want Hibernate to use your custom
SuperDuperStringType; during bootstrap you would call:
/**
* Get the names under which this type should be registered in the type registry.
*
* @return The keys under which to register this type.
*/
public String[] getRegistrationKeys();
One approach is to
use inheritance (SuperDuperStringType extends
org.hibernate.type.StringType); another is to use delegation.
159
160
Mapeamento de coleção
7.1. Coleções persistentes
Naturally Hibernate also allows to persist collections. These persistent collections can contain
almost any other Hibernate type, including: basic types, custom types, components and
references to other entities. The distinction between value and reference semantics is in this
context very important. An object in a collection might be handled with "value" semantics (its life
cycle fully depends on the collection owner), or it might be a reference to another entity with its
own life cycle. In the latter case, only the "link" between the two objects is considered to be a
state held by the collection.
Notice how in Exemplo 7.2, “Collection mapping using @OneToMany and @JoinColumn” the
instance variable parts was initialized with an instance of HashSet. This is the best way to initialize
collection valued properties of newly instantiated (non-persistent) instances. When you make the
instance persistent, by calling persist(), Hibernate will actually replace the HashSet with an
instance of Hibernate's own implementation of Set. Be aware of the following error:
161
Capítulo 7. Mapeamento de col...
Nota
Use persistent collections the same way you use ordinary Java collections.
However, ensure you understand the semantics of bidirectional associations (see
Seção 7.3.2, “Associações Bidirecionais”).
@Entity
public class Product {
@Id
public String getSerialNumber() { return serialNumber; }
void setSerialNumber(String sn) { serialNumber = sn; }
@OneToMany
@JoinColumn(name="PART_ID")
public Set<Part> getParts() { return parts; }
void setParts(Set parts) { this.parts = parts; }
}
@Entity
public class Part {
...
}
Product describes a unidirectional relationship with Part using the join column PART_ID. In this
unidirectional one to many scenario you can also use a join table as seen in Exemplo 7.3,
“Collection mapping using @OneToMany and @JoinTable”.
@Entity
public class Product {
162
How to map collections
@Id
public String getSerialNumber() { return serialNumber; }
void setSerialNumber(String sn) { serialNumber = sn; }
@OneToMany
@JoinTable(
name="PRODUCT_PARTS",
joinColumns = @JoinColumn( name="PRODUCT_ID"),
inverseJoinColumns = @JoinColumn( name="PART_ID")
)
public Set<Part> getParts() { return parts; }
void setParts(Set parts) { this.parts = parts; }
}
@Entity
public class Part {
...
}
Without describing any physical mapping (no @JoinColumn or @JoinTable), a unidirectional one
to many with join table is used. The table name is the concatenation of the owner table name,
_, and the other side table name. The foreign key name(s) referencing the owner table is the
concatenation of the owner table, _, and the owner primary key column(s) name. The foreign key
name(s) referencing the other side is the concatenation of the owner property name, _, and the
other side primary key column(s) name. A unique constraint is added to the foreign key referencing
the other side table to reflect the one to many.
Lets have a look now how collections are mapped using Hibernate mapping files. In this case the
first step is to chose the right mapping element. It depends on the type of interface. For example,
a <set> element is used for mapping properties of type Set.
<class name="Product">
<id name="serialNumber" column="productSerialNumber"/>
<set name="parts">
<key column="productSerialNumber" not-null="true"/>
<one-to-many class="Part"/>
</set>
</class>
In Exemplo 7.4, “Mapping a Set using <set>” a one-to-many association links the Product and
Part entities. This association requires the existence of a foreign key column and possibly an
index column to the Part table. This mapping loses certain semantics of normal Java collections:
• Uma instância de classes entidades contidas, podem não pertencer à mais de uma instância
da coleção.
163
Capítulo 7. Mapeamento de col...
• Uma instância da classe de entidade contida pode não aparecer em mais de um valor do índice
da coleção.
Looking closer at the used <one-to-many> tag we see that it has the following options.
<one-to-many
class="ClassName"
not-found="ignore|exception"
entity-name="EntityName"
node="element-name"
embed-xml="true|false"
/>
Note que o elemento <one-to-many> não precisa declarar qualquer coluna. Nem é necessário
especificar o nome da table em qualquer lugar.
Atenção
Apart from the <set> tag as shown in Exemplo 7.4, “Mapping a Set using <set>”, there is also
<list>, <map>, <bag>, <array> and <primitive-array> mapping elements. The <map> element
is representative:
<map
name="propertyName"
table="table_name"
schema="schema_name"
lazy="true|extra|false"
164
How to map collections
inverse="true|false"
cascade="all|none|save-update|delete|all-delete-orphan|delete-orphan"
sort="unsorted|natural|comparatorClass"
order-by="column_name asc|desc"
fetch="join|select|subselect"
batch-size="N"
access="field|property|ClassName"
optimistic-lock="true|false"
mutable="true|false"
node="element-name|."
embed-xml="true|false"
>
165
Capítulo 7. Mapeamento de col...
optimistic-lock (opcional - padrão para true): especifica que alterações para o estado da
coleção, resulta no incremento da versão da própria entidade. Para associações um-para-
muitos, é sempre bom desabilitar esta configuração.
mutable (opcional - padrão para true): um valor de false especifica que os elementos
da coleção nunca mudam. Isto permite uma otimização mínima do desempenho em alguns
casos.
After exploring the basic mapping of collections in the preceding paragraphs we will now focus
details like physical mapping considerations, indexed collections and collections of value types.
On the database level collection instances are distinguished by the foreign key of the entity that
owns the collection. This foreign key is referred to as the collection key column, or columns, of the
collection table. The collection key column is mapped by the @JoinColumn annotation respectively
the <key> XML element.
There can be a nullability constraint on the foreign key column. For most collections, this is implied.
For unidirectional one-to-many associations, the foreign key column is nullable by default, so you
may need to specify
@JoinColumn(nullable=false)
or
The foreign key constraint can use ON DELETE CASCADE. In XML this can be expressed via:
@OnDelete(action=OnDeleteAction.CASCADE)
See Seção 5.1.11.3, “Key” for more information about the <key> element.
In the following paragraphs we have a closer at the indexed collections List and Map how the
their index can be mapped in Hibernate.
166
Coleções indexadas
7.2.2.1. Lists
@Entity
public class Customer {
@Id @GeneratedValue public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
private Integer id;
@OneToMany(mappedBy="customer")
@OrderBy("number")
public List<Order> getOrders() { return orders; }
public void setOrders(List<Order> orders) { this.orders = orders; }
private List<Order> orders;
}
@Entity
public class Order {
@Id @GeneratedValue public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
private Integer id;
@ManyToOne
public Customer getCustomer() { return customer; }
public void setCustomer(Customer customer) { this.customer = customer; }
private Customer number;
}
-- Table schema
|-------------| |----------|
| Order | | Customer |
|-------------| |----------|
| id | | id |
| number | |----------|
| customer_id |
|-------------|
167
Capítulo 7. Mapeamento de col...
@Entity
public class Customer {
@Id @GeneratedValue public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
private Integer id;
@OneToMany(mappedBy="customer")
@OrderColumn(name="orders_index")
public List<Order> getOrders() { return orders; }
public void setOrders(List<Order> orders) { this.orders = orders; }
private List<Order> orders;
}
@Entity
public class Order {
@Id @GeneratedValue public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
private Integer id;
@ManyToOne
public Customer getCustomer() { return customer; }
public void setCustomer(Customer customer) { this.customer = customer; }
private Customer number;
}
-- Table schema
|--------------| |----------|
| Order | | Customer |
|--------------| |----------|
| id | | id |
| number | |----------|
| customer_id |
| orders_order |
|--------------|
Nota
168
Coleções indexadas
index value of the first element (aka as base index). The usual value is 0 or 1. The
default is 0 like in Java.
Looking again at the Hibernate mapping file equivalent, the index of an array or list is always of
type integer and is mapped using the <list-index> element. The mapped column contains
sequential integers that are numbered from zero by default.
<list-index
column="column_name"
base="0|1|..."/>
Se sua tabela não possui uma coluna de índice e você ainda quiser usar a Lista como tipo de
propriedade, você deve mapeiar a propriedade como uma <bag> do Hibernate. Uma bag não
mantém sua ordem quando é recuperadada do banco de dados, mas pode ser escolhida de forma
opcional ou ordenada.
7.2.2.2. Maps
The question with Maps is where the key value is stored. There are everal options. Maps can
borrow their keys from one of the associated entity properties or have dedicated columns to store
an explicit key.
To use one of the target entity property as a key of the map, use @MapKey(name="myProperty"),
where myProperty is a property name in the target entity. When using @MapKey without the name
attribuate, the target entity primary key is used. The map key uses the same column as the property
pointed out. There is no additional column defined to hold the map key, because the map key
represent a target property. Be aware that once loaded, the key is no longer kept in sync with the
property. In other words, if you change the property value, the key will not change automatically
in your Java model.
Exemplo 7.10. Use of target entity property as map key via @MapKey
@Entity
public class Customer {
@Id @GeneratedValue public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
private Integer id;
@OneToMany(mappedBy="customer")
@MapKey(name="number")
169
Capítulo 7. Mapeamento de col...
@Entity
public class Order {
@Id @GeneratedValue public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
private Integer id;
@ManyToOne
public Customer getCustomer() { return customer; }
public void setCustomer(Customer customer) { this.customer = customer; }
private Customer number;
}
-- Table schema
|-------------| |----------|
| Order | | Customer |
|-------------| |----------|
| id | | id |
| number | |----------|
| customer_id |
|-------------|
Alternatively the map key is mapped to a dedicated column or columns. In order to customize the
mapping use one of the following annotations:
• @MapKeyColumn if the map key is a basic type. If you don't specify the column name, the name
of the property followed by underscore followed by KEY is used (for example orders_KEY).
You can also use @MapKeyClass to define the type of the key if you don't use generics.
@Entity
public class Customer {
@Id @GeneratedValue public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
private Integer id;
@OneToMany @JoinTable(name="Cust_Order")
@MapKeyColumn(name="orders_number")
170
Coleções indexadas
@Entity
public class Order {
@Id @GeneratedValue public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
private Integer id;
@ManyToOne
public Customer getCustomer() { return customer; }
public void setCustomer(Customer customer) { this.customer = customer; }
private Customer number;
}
-- Table schema
|-------------| |----------| |---------------|
| Order | | Customer | | Cust_Order |
|-------------| |----------| |---------------|
| id | | id | | customer_id |
| number | |----------| | order_id |
| customer_id | | orders_number |
|-------------| |---------------|
Nota
Using Hibernate mapping files there exists equivalent concepts to the descibed annotations. You
have to use <map-key>, <map-key-many-to-many> and <composite-map-key>. <map-key> is
used for any basic type, <map-key-many-to-many> for an entity reference and <composite-map-
key> for a composite type.
<map-key
column="column_name"
type="type_name"
node="@attribute-name"
length="N"/>
171
Capítulo 7. Mapeamento de col...
<map-key-many-to-many
column="column_name"
column (opcional): o nome de uma coluna de chave exterior para os valores do índice de
coleção.
formula (opcional): uma fórmula SQ usada para avaliar a chave exterior da chave do mapa.
class (requerido): a classe da entidade usada como chave do mapa.
@Entity
public class User {
[...]
public String getLastname() { ...}
@ElementCollection
@CollectionTable(name="Nicknames", joinColumns=@JoinColumn(name="user_id"))
@Column(name="nickname")
public Set<String> getNicknames() { ... }
}
The collection table holding the collection data is set using the @CollectionTable annotation.
If omitted the collection table name defaults to the concatenation of the name of the containing
entity and the name of the collection attribute, separated by an underscore. In our example, it
would be User_nicknames.
The column holding the basic type is set using the @Column annotation. If omitted, the column
name defaults to the property name: in our example, it would be nicknames.
But you are not limited to basic types, the collection type can be any embeddable
object. To override the columns of the embeddable object in the collection table, use the
@AttributeOverride annotation.
172
Collections of basic types and embeddable objects
@Entity
public class User {
[...]
public String getLastname() { ...}
@ElementCollection
@CollectionTable(name="Addresses", joinColumns=@JoinColumn(name="user_id"))
@AttributeOverrides({
@AttributeOverride(name="street1", column=@Column(name="fld_street"))
})
public Set<Address> getAddresses() { ... }
}
@Embeddable
public class Address {
public String getStreet1() {...}
[...]
}
Nota
@Entity
public class User {
@ElementCollection
@AttributeOverrides({
@AttributeOverride(name="key.street1", column=@Column(name="fld_street")),
@AttributeOverride(name="value.stars", column=@Column(name="fld_note"))
})
public Map<Address,Rating> getFavHomes() { ... }
Nota
We recommend you to
migrate from
@org.hibernate.annotations.CollectionOfElements to the new
@ElementCollection annotation.
Using the mapping file approach a collection of values is mapped using the <element> tag. For
example:
173
Capítulo 7. Mapeamento de col...
Exemplo 7.16. <element> tag for collection values using mapping files
<element
column="column_name"
type="typename"
length="L"
precision="P"
scale="S"
not-null="true|false"
unique="true|false"
node="element-name"
/>
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
@JoinColumn(name="CUST_ID")
@Sort(type = SortType.COMPARATOR, comparator = TicketComparator.class)
public SortedSet<Ticket> getTickets() {
return tickets;
}
Using Hibernate mapping files you specify a comparator in the mapping file with <sort>:
<set name="aliases"
table="person_aliases"
sort="natural">
<key column="person"/>
174
Coleções escolhidas
Dica
If you want the database itself to order the collection elements, use the order-by attribute of set,
bag or map mappings. This solution is implemented using LinkedHashSet or LinkedHashMap and
performs the ordering in the SQL query and not in the memory.
Nota
Note que o valor da função order-by é uma ordenação SQL e não uma
ordenação.
Associações podem também ser escolhidas por algum critério arbritrário em tempo de espera
usando uma coleção filter():
175
Capítulo 7. Mapeamento de col...
Um-para-muitos
conjunto ou bag de valor em um dos lados, valor único do outro
Muitos-para-muitos
Conjunto ou bag com valor em ambos os lados
Often there exists a many to one association which is the owner side of a bidirectional
relationship. The corresponding one to many association is in this case annotated by
@OneToMany(mappedBy=...)
@Entity
public class Troop {
@OneToMany(mappedBy="troop")
public Set<Soldier> getSoldiers() {
...
}
@Entity
public class Soldier {
@ManyToOne
@JoinColumn(name="troop_fk")
public Troop getTroop() {
...
}
Troop has a bidirectional one to many relationship with Soldier through the troop property. You
don't have to (must not) define any physical mapping in the mappedBy side.
To map a bidirectional one to many, with the one-to-many side as the owning side, you have to
remove the mappedBy element and set the many to one @JoinColumn as insertable and updatable
to false. This solution is not optimized and will produce additional UPDATE statements.
@Entity
176
Associações Bidirecionais
@Entity
public class Soldier {
@ManyToOne
@JoinColumn(name="troop_fk", insertable=false, updatable=false)
public Troop getTroop() {
...
}
How does the mappping of a bidirectional mapping look like in Hibernate mapping xml? There
you define a bidirectional one-to-many association by mapping a one-to-many association to
the same table column(s) as a many-to-one association and declaring the many-valued end
inverse="true".
<class name="Parent">
<id name="id" column="parent_id"/>
....
<set name="children" inverse="true">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
</class>
<class name="Child">
<id name="id" column="child_id"/>
....
<many-to-one name="parent"
class="Parent"
column="parent_id"
not-null="true"/>
</class>
Mapear apenas uma das pontas da associação com inverse="true" não afeta as operações
em cascata, uma vez que isto é um conceito ortogonal.
A many-to-many association is defined logically using the @ManyToMany annotation. You also have
to describe the association table and the join conditions using the @JoinTable annotation. If the
association is bidirectional, one side has to be the owner and one side has to be the inverse end
(ie. it will be ignored when updating the relationship values in the association table):
177
Capítulo 7. Mapeamento de col...
@Entity
public class Employer implements Serializable {
@ManyToMany(
targetEntity=org.hibernate.test.metadata.manytomany.Employee.class,
cascade={CascadeType.PERSIST, CascadeType.MERGE}
)
@JoinTable(
name="EMPLOYER_EMPLOYEE",
joinColumns=@JoinColumn(name="EMPER_ID"),
inverseJoinColumns=@JoinColumn(name="EMPEE_ID")
)
public Collection getEmployees() {
return employees;
}
...
}
@Entity
public class Employee implements Serializable {
@ManyToMany(
cascade = {CascadeType.PERSIST, CascadeType.MERGE},
mappedBy = "employees",
targetEntity = Employer.class
)
public Collection getEmployers() {
return employers;
}
}
In this example @JoinTable defines a name, an array of join columns, and an array of inverse join
columns. The latter ones are the columns of the association table which refer to the Employee
primary key (the "other side"). As seen previously, the other side don't have to (must not) describe
the physical mapping: a simple mappedBy argument containing the owner side property name bind
the two.
As any other annotations, most values are guessed in a many to many relationship. Without
describing any physical mapping in a unidirectional many to many the following rules applied. The
table name is the concatenation of the owner table name, _ and the other side table name. The
foreign key name(s) referencing the owner table is the concatenation of the owner table name, _
and the owner primary key column(s). The foreign key name(s) referencing the other side is the
concatenation of the owner property name, _, and the other side primary key column(s). These
are the same rules used for a unidirectional one to many relationship.
@Entity
178
Associações Bidirecionais
@Entity
public class City {
... //no bidirectional relationship
}
A Store_City is used as the join table. The Store_id column is a foreign key to the Store table.
The implantedIn_id column is a foreign key to the City table.
Without describing any physical mapping in a bidirectional many to many the following rules
applied. The table name is the concatenation of the owner table name, _ and the other side table
name. The foreign key name(s) referencing the owner table is the concatenation of the other side
property name, _, and the owner primary key column(s). The foreign key name(s) referencing the
other side is the concatenation of the owner property name, _, and the other side primary key
column(s). These are the same rules used for a unidirectional one to many relationship.
@Entity
public class Store {
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
public Set<Customer> getCustomers() {
...
}
}
@Entity
public class Customer {
@ManyToMany(mappedBy="customers")
public Set<Store> getStores() {
...
}
}
A Store_Customer is used as the join table. The stores_id column is a foreign key to the Store
table. The customers_id column is a foreign key to the Customer table.
Using Hibernate mapping files you can map a bidirectional many-to-many association by mapping
two many-to-many associations to the same database table and declaring one end as inverse.
Nota
You cannot select an indexed collection.
179
Capítulo 7. Mapeamento de col...
Exemplo 7.27, “Many to many association using Hibernate mapping files” shows a bidirectional
many-to-many association that illustrates how each category can have many items and each item
can be in many categories:
<class name="Category">
<id name="id" column="CATEGORY_ID"/>
...
<bag name="items" table="CATEGORY_ITEM">
<key column="CATEGORY_ID"/>
<many-to-many class="Item" column="ITEM_ID"/>
</bag>
</class>
<class name="Item">
<id name="id" column="ITEM_ID"/>
...
As mudanças feitas somente de um lado da associação não são persistidas. Isto significa que
o Hibernate tem duas representações na memória para cada associação bidirecional, uma
associação de A para B e uma outra associação de B para A. Isto é mais fácil de compreender
se você pensa sobre o modelo de objetos do Java e como criamos um relacionamento muitos
para muitos em Java:
There are some additional considerations for bidirectional mappings with indexed collections
(where one end is represented as a <list> or <map>) when using Hibernate mapping files. If there
180
Associações bidirecionais com coleções indexadas
is a property of the child class that maps to the index column you can use inverse="true" on
the collection mapping:
<class name="Parent">
<id name="id" column="parent_id"/>
....
<map name="children" inverse="true">
<key column="parent_id"/>
<map-key column="name"
type="string"/>
<one-to-many class="Child"/>
</map>
</class>
<class name="Child">
<id name="id" column="child_id"/>
....
<property name="name"
not-null="true"/>
<many-to-one name="parent"
class="Parent"
column="parent_id"
not-null="true"/>
</class>
Mas, se não houver nenhuma propriedade na classe filha, não podemos ver essa associação
como verdadeiramente bidirecional (há uma informação disponível em um lado da associação
que não está disponível no extremo oposto). Nesse caso, nós não podemos mapear a coleção
usando inverse="true". Devemos usar o seguinte mapeamento:
<class name="Parent">
<id name="id" column="parent_id"/>
....
<map name="children">
<key column="parent_id"
not-null="true"/>
<map-key column="name"
type="string"/>
<one-to-many class="Child"/>
</map>
</class>
<class name="Child">
<id name="id" column="child_id"/>
....
<many-to-one name="parent"
class="Parent"
181
Capítulo 7. Mapeamento de col...
column="parent_id"
insert="false"
update="false"
not-null="true"/>
</class>
Veja que neste mapeamento, o lado de coleção válida da associação é responsável pela
atualização da chave exterior.
Há três meios possíveis de se mapear uma associação ternária. Uma é usar um Map com uma
associação como seu índice:
@Entity
public class Company {
@Id
int id;
...
@OneToMany // unidirectional
@MapKeyJoinColumn(name="employee_id")
Map<Employee, Contract> contracts;
}
// or
<map name="contracts">
<key column="employer_id" not-null="true"/>
<map-key-many-to-many column="employee_id" class="Employee"/>
<one-to-many class="Contract"/>
</map>
A second approach is to remodel the association as an entity class. This is the most common
approach. A final alternative is to use composite elements, which will be discussed later.
The majority of the many-to-many associations and collections of values shown previously all
map to tables with composite keys, even though it has been suggested that entities should have
synthetic identifiers (surrogate keys). A pure association table does not seem to benefit much
from a surrogate key, although a collection of composite values might. For this reason Hibernate
provides a feature that allows you to map many-to-many associations and collections of values
to a table with a surrogate key.
O elemento <idbag> permite mapear um List (ou uma Collection) com uma semântica de
bag. Por exemplo:
182
Exemplos de coleções
O <idbag> possui um gerador de id sintético, igual a uma classe de entidade. Uma chave
substituta diferente é associada para cada elemento de coleção. Porém, o Hibernate não provê
de nenhum mecanismo para descobrir qual a chave substituta de uma linha em particular.
// getter/setter
...
}
// getter/setter
...
}
Se cada Filho tiver no máximo um Pai, o mapeamento natural é uma associação um para muitos:
183
Capítulo 7. Mapeamento de col...
@OneToMany
private Set<Child> children;
// getter/setter
...
}
// getter/setter
...
}
<hibernate-mapping>
<class name="Parent">
<id name="id">
<generator class="sequence"/>
</id>
<set name="children">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
</class>
<class name="Child">
<id name="id">
<generator class="sequence"/>
</id>
<property name="name"/>
</class>
</hibernate-mapping>
184
Exemplos de coleções
@OneToMany(mappedBy="parent")
private Set<Child> children;
// getter/setter
...
}
@ManyToOne
private Parent parent;
// getter/setter
...
}
<hibernate-mapping>
<class name="Parent">
<id name="id">
<generator class="sequence"/>
</id>
<set name="children" inverse="true">
<key column="parent_id"/>
185
Capítulo 7. Mapeamento de col...
<one-to-many class="Child"/>
</set>
</class>
<class name="Child">
<id name="id">
<generator class="sequence"/>
</id>
<property name="name"/>
<many-to-one name="parent" class="Parent" column="parent_id" not-null="true"/>
</class>
</hibernate-mapping>
Alternatively, if this association must be unidirectional you can enforce the NOT NULL constraint.
@OneToMany(optional=false)
private Set<Child> children;
// getter/setter
...
}
// getter/setter
...
186
Exemplos de coleções
<hibernate-mapping>
<class name="Parent">
<id name="id">
<generator class="sequence"/>
</id>
<set name="children">
<key column="parent_id" not-null="true"/>
<one-to-many class="Child"/>
</set>
</class>
<class name="Child">
<id name="id">
<generator class="sequence"/>
</id>
<property name="name"/>
</class>
</hibernate-mapping>
On the other hand, if a child has multiple parents, a many-to-many association is appropriate.
@ManyToMany
private Set<Child> children;
// getter/setter
...
}
// getter/setter
187
Capítulo 7. Mapeamento de col...
...
}
<hibernate-mapping>
<class name="Parent">
<id name="id">
<generator class="sequence"/>
</id>
<set name="children" table="childset">
<key column="parent_id"/>
<many-to-many class="Child" column="child_id"/>
</set>
</class>
<class name="Child">
<id name="id">
<generator class="sequence"/>
</id>
<property name="name"/>
</class>
</hibernate-mapping>
For more examples and a complete explanation of a parent/child relationship mapping, see
Capítulo 24, Exemplo: Pai/Filho for more information. Even more complex association mappings
are covered in the next chapter.
188
Mapeamento de associações
8.1. Introdução
Os mapeamentos de associações são, geralmente, os mais difíceis de se acertar. Nesta
seção nós examinaremos pelos casos canônicos um por um, começando com mapeamentos
unidirecionais e considerando os casos bidirecionais. Usaremos Person e Address em todos os
exemplos.
O uso de chaves externas anuláveis não é considerado uma boa prática na modelagem de dados
tradicional, assim todos os nossos exemplos usam chaves externas anuláveis. Esta não é uma
exigência do Hibernate, e todos os mapeamentos funcionarão se você remover as restrições de
anulabilidade.
8.2.1. Muitos-para-um
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class
>
create table Person ( personId bigint not null primary key, addressId bigint not null )
create table Address ( addressId bigint not null primary key )
189
Capítulo 8. Mapeamento de ass...
8.2.2. Um-para-um
Uma associação unidirecional um-para-um em uma chave externa é quase idêntica. A única
diferença é a restrição única na coluna.
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
unique="true"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class
>
create table Person ( personId bigint not null primary key, addressId bigint not null unique )
create table Address ( addressId bigint not null primary key )
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
</class>
<class name="Address">
<id name="id" column="personId">
<generator class="foreign">
<param name="property"
>person</param>
</generator>
</id>
<one-to-one name="person" constrained="true"/>
</class
>
190
Um-para-muitos
8.2.3. Um-para-muitos
Uma associação unidirecional um-para-muitos em uma chave externa é um caso muito incomum,
e realmente não é recomendada.
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses">
<key column="personId"
not-null="true"/>
<one-to-many class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class
>
Acreditamos ser melhor usar uma tabela associativa para este tipo de associação.
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses" table="PersonAddress">
191
Capítulo 8. Mapeamento de ass...
<key column="personId"/>
<many-to-many column="addressId"
unique="true"
class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class
>
8.3.2. Muitos-para-um
Uma associação unidirecional muitos-para-um em uma tabela associativa é bastante comum
quando a associação for opcional.
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<join table="PersonAddress"
optional="true">
<key column="personId" unique="true"/>
<many-to-one name="address"
column="addressId"
not-null="true"/>
</join>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class
>
192
Um-para-um
8.3.3. Um-para-um
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<join table="PersonAddress"
optional="true">
<key column="personId"
unique="true"/>
<many-to-one name="address"
column="addressId"
not-null="true"
unique="true"/>
</join>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class
>
8.3.4. Muitos-para-muitos
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses" table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId"
class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
193
Capítulo 8. Mapeamento de ass...
<generator class="native"/>
</id>
</class
>
8.4.1. Um-para-muitos/muitos-para-um
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<set name="people" inverse="true">
<key column="addressId"/>
<one-to-many class="Person"/>
</set>
</class
>
create table Person ( personId bigint not null primary key, addressId bigint not null )
create table Address ( addressId bigint not null primary key )
Se você usar uma List ou outra coleção indexada, você precisará especificar a coluna key da
chave externa como not null. O Hibernate administrará a associação do lado da coleção para
194
Um-para-um
que seja mantido o índice de cada elemento da coleção (fazendo com que o outro lado seja
virtualmente inverso ajustando update="false" e insert="false"):
<class name="Person">
<id name="id"/>
...
<many-to-one name="address"
column="addressId"
not-null="true"
insert="false"
update="false"/>
</class>
<class name="Address">
<id name="id"/>
...
<list name="people">
<key column="addressId" not-null="true"/>
<list-index column="peopleIdx"/>
<one-to-many class="Person"/>
</list>
</class
>
Caso uma coluna chave externa adjacente for NOT NULL, é importante que você defina not-
null="true" no elemento <key> no mapeamento na coleção se a coluna de chave externa para
NOT NULL. Não declare como not-null="true" apenas um elemento aninhado <column>, mas
sim o elemento <key>.
8.4.2. Um-para-um
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
unique="true"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<one-to-one name="person"
property-ref="address"/>
</class
>
195
Capítulo 8. Mapeamento de ass...
create table Person ( personId bigint not null primary key, addressId bigint not null unique )
create table Address ( addressId bigint not null primary key )
Uma associação bidirecional um para um em uma chave primária usa um gerador de id especial:
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<one-to-one name="address"/>
</class>
<class name="Address">
<id name="id" column="personId">
<generator class="foreign">
<param name="property"
>person</param>
</generator>
</id>
<one-to-one name="person"
constrained="true"/>
</class
>
8.5.1. Um-para-muitos/muitos-para-um
Segue abaixo uma amostra da associação bidirecional um para muitos em uma tabela de união.
Veja que inverse="true" pode ser colocado em qualquer ponta da associação, na coleção, ou
na união.
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses"
table="PersonAddress">
<key column="personId"/>
196
Um para um
<many-to-many column="addressId"
unique="true"
class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<join table="PersonAddress"
inverse="true"
optional="true">
<key column="addressId"/>
<many-to-one name="person"
column="personId"
not-null="true"/>
</join>
</class
>
8.5.2. Um para um
Uma associação bidirecional um-para-um em uma tabela de união é algo bastante incomum,
mas possível.
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<join table="PersonAddress"
optional="true">
<key column="personId"
unique="true"/>
<many-to-one name="address"
column="addressId"
not-null="true"
unique="true"/>
</join>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<join table="PersonAddress"
optional="true"
197
Capítulo 8. Mapeamento de ass...
inverse="true">
<key column="addressId"
unique="true"/>
<many-to-one name="person"
column="personId"
not-null="true"
unique="true"/>
</join>
</class
>
8.5.3. Muitos-para-muitos
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses" table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId"
class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<set name="people" inverse="true" table="PersonAddress">
<key column="addressId"/>
<many-to-many column="personId"
class="Person"/>
</set>
</class
>
198
Mapeamento de associações mais complexas
<properties name="currentAccountKey">
<property name="accountNumber" type="string" not-null="true"/>
<property name="currentAccount" type="boolean">
<formula
>case when effectiveEndDate is null then 1 else 0 end</formula>
</property>
</properties>
<property name="effectiveEndDate" type="date"/>
<property name="effectiveStateDate" type="date" not-null="true"/>
Então nós podemos mapear uma associação para a instância atual, aquela com
effectiveEndDate nulo, usando:
<many-to-one name="currentAccountInfo"
property-ref="currentAccountKey"
class="AccountInfo">
<column name="accountNumber"/>
<formula
>'1'</formula>
</many-to-one
>
<join>
<key column="employeeId"/>
<subselect>
select employeeId, orgId
from Employments
group by orgId
having startDate = max(startDate)
</subselect>
<many-to-one name="mostRecentEmployer"
class="Organization"
199
Capítulo 8. Mapeamento de ass...
column="orgId"/>
</join
>
Esta funcionalidade permite um grau de criatividade e flexibilidade, mas geralmente é mais prático
tratar estes tipos de casos, usando uma pesquisa HQL ou uma pesquisa por critério.
200
Mapeamento de Componentes
A noção de componente é re-utilizada em vários contextos diferentes, para propósitos diferentes,
pelo Hibernate.
201
Capítulo 9. Mapeamento de Com...
Agora Name pode ser persistido como um componente de Person. Note que Name define métodos
getter e setter para suas propriedades persistentes, mas não necessita declarar nenhuma
interface ou propriedades identificadoras.
A tabela person teria as seguintes colunas pid, birthday, initial, first and last.
Assim como todos tipos por valor, componentes não suportam referências cruzadas. Em outras
palavras, duas persons podem ter o mesmo nome, mas os dois objetos person podem ter dois
objetos de nome independentes, apenas "o mesmo" por valor. A semântica dos valores null de
um componente são ad hoc. No recarregameno do conteúdo do objeto, o Hibernate entenderá
que se todas as colunas do componente são null, então todo o componente é null. Isto seria o
certo para a maioria dos propósitos.
202
Coleções de objetos dependentes
Importante
Elementos compostos podem conter componentes mas não coleções. Se o seu elemento
composto tiver componentes , use a tag <nested-composite-element>. Este é um caso bastante
exótico – coleções de componentes que por si própria possui componentes. Neste momento você
deve estar se perguntando se uma associação de um-para-muitos seria mais apropriada. Tente
remodelar o elemento composto como uma entidade – mas note que mesmo pensando que o
modelo Java é o mesmo, o modelo relacional e a semântica de persistência ainda são diferentes.
203
Capítulo 9. Mapeamento de Com...
Não pode haver uma referência de compra no outro lado, para a navegação da associação
bidirecional. Lembre-se que componentes são tipos por valor e não permitem referências
compartilhadas. Uma classe Purchase simples pode estar no conjunto de uma classe Order, mas
ela não pode ser referenciada por Item no mesmo momento.
Elementos compostos podem aparecer em pesquisas usando a mesma sintaxe assim como
associações para outras entidades.
204
Componentes como identificadores compostos
Nota
Você não pode usar um IdentifierGenerator para gerar chaves compostas. Ao invés disso, o
aplicativo deve gerenciar seus próprios identificadores.
<class name="OrderLine">
<property name="name"/>
</class
>
Agora, qualquer chave exterior referenciando a tabela OrderLine também será composta. Você
deve declarar isto em seus mapeamentos para outras classes. Uma associação para OrderLine
seria mapeada dessa forma:
205
Capítulo 9. Mapeamento de Com...
<column name="lineId"/>
<column name="orderId"/>
<column name="customerId"/>
</many-to-one
>
Dica
Uma associação many-to-many para many-to-many também usa a chave estrangeira composta:
<set name="undeliveredOrderLines">
<key column name="warehouseId"/>
<many-to-many class="OrderLine">
<column name="lineId"/>
<column name="orderId"/>
<column name="customerId"/>
</many-to-many>
</set
>
Se OrderLine possui uma coleção, ela também tem uma chave externa composta.
<class name="OrderLine">
....
....
<list name="deliveryAttempts">
<key
> <!-- a collection inherits the composite key type -->
<column name="lineId"/>
<column name="orderId"/>
206
Componentes Dinâmicos
<column name="customerId"/>
</key>
<list-index column="attemptId" base="1"/>
<composite-element class="DeliveryAttempt">
...
</composite-element>
</set>
</class
>
<dynamic-component name="userAttributes">
<property name="foo" column="FOO" type="string"/>
<property name="bar" column="BAR" type="integer"/>
<many-to-one name="baz" class="Baz" column="BAZ_ID"/>
</dynamic-component
>
207
208
Mapeamento de Herança
10.1. As três estratégias
O Hibernate suporta as três estratégias básicas de mapeamento de herança:
• polimorfismo implícito
<hibernate-mapping>
<subclass name="DomesticCat" extends="Cat" discriminator-value="D">
<property name="name" type="string"/>
</subclass>
</hibernate-mapping
>
209
Capítulo 10. Mapeamento de He...
É requisitado exatamente uma tabela. Existe uma grande limitação desta estratégia de
mapeamento: colunas declaradas por subclasses, tais como CCTYPE, podem não ter restrições
NOT NULL.
210
Tabela por subclasse: usando um discriminador
São necessárias quatro tabelas. As três tabelas subclasses possuem associação de chave
primária para a tabela de superclasse, desta maneira o modelo relacional é atualmente uma
associação de um-para-um.
A declaração opcional fetch="select" diz ao Hibernate para não buscar os dados da subclasse
ChequePayment, quando usar uma união externa pesquisando a superclasse.
Você pode até mesmo mesclar a estratégia de tabela por hierarquia e tabela por subclasse
usando esta abordagem:
211
Capítulo 10. Mapeamento de He...
Para qualquer uma dessas estratégias de mapeamento, uma associação polimórfica para a
classe raíz Payment deve ser mapeada usando <many-to-one>.
Existem duas formas que poderíamos usar a respeito da estratégia de mapeamento de tabela
por classe concreta. A primeira é usar <union-subclass>.
<class name="Payment">
<id name="id" type="long" column="PAYMENT_ID">
<generator class="sequence"/>
</id>
<property name="amount" column="AMOUNT"/>
...
<union-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
<property name="creditCardType" column="CCTYPE"/>
...
</union-subclass>
<union-subclass name="CashPayment" table="CASH_PAYMENT">
...
</union-subclass>
<union-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
...
</union-subclass>
</class
>
212
Tabela por classe concreta usando polimorfismo implícito
Três tabelas estão envolvidas para as subclasses. Cada tabela define colunas para todas as
propriedades da classe, incluindo propriedades herdadas.
Se sua superclasse é abstrata, mapeie-a com abstract="true". Claro, que se ela não for
abstrata, uma tabela adicional (padrão para PAYMENT no exemplo acima), será necessária para
segurar as instâncias da superclasse.
Veja que em nenhum lugar mencionamos a interface Payment explicitamente. Note também
que propriedades de Payment são mapeadas em cada uma das subclasses. Se você quiser
evitar duplicação, considere usar entidades de XML (ex. [ <!ENTITY allproperties SYSTEM
"allproperties.xml"> ] na declaração do DOCTYPE e & allproperties; no mapeamento).
A desvantagem dessa abordagem é que o Hibernate não gera UNIONs de SQL quando executa
pesquisas polimórficas.
213
Capítulo 10. Mapeamento de He...
Para essa estratégia, uma associação polimórfica para Payment geralmente é mapeada usando
<any>.
Existe ainda um item a ser observado sobre este mapeamento. Como as subclasses são
mapeadas em seu próprio elemento <class>, e como o Payment é apenas uma interface, cada
uma das subclasses pode ser facilmente parte de uma outra hierarquia de herança! (E você ainda
pode usar pesquisas polimórficas em cima da interface Payment.)
214
Limitações
Mais uma vez, nós não mencionamos Payment explicitamente. Se nós executarmos uma
pesquisa em cima da interface Payment, por exemplo, from Payment – o Hibernate retorna
automaticamente instâncias de CreditCardPayment (e suas subclasses, desde que elas
também implementem Payment), CashPayment e ChequePayment mas não as instâncias de
NonelectronicTransaction.
10.2. Limitações
Existem certas limitações para a abordagem do "polimorfismo implícito" comparada com a
estratégia de mapeamento da tabela por classe concreta. Existe uma limitação um tanto menos
restritiva para mapeamentos <union-subclass>.
215
216
Trabalhando com objetos
O Hibernate é uma solução completa de mapeamento objeto/relacional que não apenas poupa
o desenvolvedor dos detalhes de baixo nível do sistema de gerenciamento do banco de dados,
como também oferece um gerenciamento de estado para objetos. Isto é, ao contrário do
gerenciamento de instruções SQL em camadas de persistência JDBC/SQL comuns, uma visão
natural da persistência orientada a objetos em aplicações Java.
• Transient - um objeto é transiente se ele foi instanciando usando apenas o operador new e
não foi associado a uma Session do Hibernate. Ele não possui uma representação persistente
no banco de dados e não lhe foi atribuído nenhum identificador. Instâncias transientes serão
destruídas pelo coletor de lixo se a aplicação não mantiver sua referência. Use uma Session
do Hibernate para tornar o objeto persistente (e deixe o Hibernate gerenciar as instruções SQL
que serão necessárias para executar esta transição).
• Detached – uma instância desanexada é um objeto que foi persistido, mas sua Session
foi fechada. A referência ao objeto continua válida, é claro, e a instância desanexada pode
ser acoplada a uma nova Session no futuro, tornando-o novamente persistente (e todas as
modificações sofridas). Essa característica habilita um modelo de programação para unidades
de trabalho de longa execução, que requeira um tempo de espera do usuário. Podemos chamá-
las de transações da aplicação, ou seja, uma unidade de trabalho do ponto de vista do usuário.
Agora iremos discutir os estados e suas transições (e os métodos do Hibernate que disparam
uma transição) em mais detalhes.
217
Capítulo 11. Trabalhando com ...
Se Cat possui um identificador gerado, o identificador é gerado e atribuído à cat quando save()
for chamado. Se Cat possuir um identificador Associado, ou uma chave composta, o identificador
deverá ser atribuído à instância de cat antes que save() seja chamado. Pode-se usar também
persist() ao invés de save(), com a semântica definida no novo esboço do EJB3.
• persist() faz uma instância transciente persistente. No entanto, isto não garante que o valor
do identificador será determinado à instância persistente imediatamente, pois a determinação
pode acontecer no período de limpeza. O persist() também garante que isto não executará
uma declaração INSERT caso esta seja chamada fora dos limites da transação. Isto é útil em
transações de longa-execução com um contexto de Sessão/persistência estendido.
• save() garante retornar um identificador. Caso um INSERT necessita ser executado para
obter o identificador (ex.: gerador "identidade" e não "seqüência"), este INSERT acontece
imediatamente, independente de você estar dentro ou fora da transação. Isto é problemático
numa conversação de longa execução com um contexto de Sessão/persistência estendido.
Se o objeto persistido tiver associado objetos (ex.: a coleção kittens no exemplo anterior), esses
objetos podem se tornar persistentes em qualquer ordem que se queira, a não ser que se tenha
uma restrição NOT NULL em uma coluna de chave estrangeira. Nunca há risco de violação de
restrições de chave estrangeira. Assim, pode-se violar uma restrição NOT NULL se save() for
usado nos objetos em uma ordem errada.
Geralmente você não precisa se preocupar com esses detalhes, pois muito provavelmente
usará a característica de persistência transitiva do Hibernate para salvar os objetos associados
automaticamente. Assim, enquanto uma restrição NOT NULL não ocorrer, o Hibernate tomará
conta de tudo. Persistência transitiva será discutida mais adiante nesse mesmo capítulo.
218
Carregando o objeto
Repare que load() irá lançar uma exceção irrecuperável se não houver na tabela no banco de
dados um registro que combine. Se a classe for mapeada com um proxy, load() simplesmente
retorna um proxy não inicializado e realmente não chamará o banco de dados até que um
método do proxy seja invocado. Esse comportamento é muito útil para criar uma associação
com um objeto sem que realmente o carregue do bando de dados. Isto também permite que
sejam carregadas múltiplas instâncias como um grupo se o batch-size estiver definido para o
mapeamento da classe.
Se você não tiver certeza da existência do registro no banco, você deve usar o método get(),
que consulta o banco imediatamente e retorna um null se não existir o registro.
Também pode-se carregar um objeto usando SELECT ... FOR UPDATE, usando um LockMode.
Veja a documentação da API para maiores informações.
219
Capítulo 11. Trabalhando com ...
Note que quaisquer instâncias associadas ou que contenham coleções, não são selecionados
FOR UPDATE, a não ser que você decida especificar um lock ou all como um estilo cascata
para a associação.
sess.save(cat);
sess.flush(); //force the SQL INSERT
sess.refresh(cat); //re-read the state (after the trigger executes)
How much does Hibernate load from the database and how many SQL SELECTs will it use? This
depends on the fetching strategy. This is explained in Seção 21.1, “Estratégias de Busca ”.
11.4. Consultando
Se o identificador do objeto que se está buscando não for conhecido, será necessário realizar
uma consulta. O Hibernate suporta uma linguagem de consulta (HQL) orientada a objetos fáceis
de usar, porém poderosos. Para criação via programação de consultas, o Hibernate suporta
características sofisticadas de consulta por Critério e Exemplo (QBCe QBE). Pode-se também
expressar a consulta por meio de SQL nativa do banco de dados, com suporte opcional do
Hibernate para conversão do conjunto de resultados em objetos.
Consultas HQL e SQL nativas são representadas por uma instância de org.hibernate.Query.
Esta interface oferece métodos para associação de parâmetros, tratamento de conjunto de
resultados e para a execução de consultas reais. Você pode obter uma Query usando a Session
atual:
220
Executando consultas
.setEntity(0, izi)
.uniqueResult();]]
Geralmente uma consulta é executada ao invocar list().O resultado da consulta será carregado
completamente em uma coleção na memória. Instâncias de entidades recuperadas por uma
consulta estão no estado persistente. O uniqueResult() oferece um atalho se você souber
previamente, que a consulta retornará apenas um único objeto. Repare que consultas que fazem
uso da busca antecipada (eager fetching) de coleções, geralmente retornam duplicatas dos
objetos raiz, mas com suas coleções inicializadas. Pode-se filtrar estas duplicatas através de um
simples Set.
// fetch ids
Iterator iter = sess.createQuery("from eg.Qux q order by q.likeliness").iterate();
while ( iter.hasNext() ) {
Qux qux = (Qux) iter.next(); // fetch the object
// something we couldnt express in the query
if ( qux.calculateComplicatedAlgorithm() ) {
// delete the current instance
iter.remove();
// dont need to process the rest
break;
}
}
Algumas vezes as consultas do Hibernate retornam tuplas de objetos. Cada tupla é retornada
como uma matriz:
221
Capítulo 11. Trabalhando com ...
while ( kittensAndMothers.hasNext() ) {
Object[] tuple = (Object[]) kittensAndMothers.next();
Cat kitten = (Cat) tuple[0];
Cat mother = (Cat) tuple[1];
....
}
As consultas devem especificar uma propriedade da classe na cláusula select. Elas também
podem chamar funções SQL de agregações. Propriedades ou agregações são consideradas
resultados agregados e não entidades no estado persistente.
while ( results.hasNext() ) {
Object[] row = (Object[]) results.next();
Color type = (Color) row[0];
Date oldest = (Date) row[1];
Integer count = (Integer) row[2];
.....
}
Métodos em Consulta são oferecidos para valores de vínculo para parâmetros nomeados ou de
estilo JDBC ?. Ao contrário do JDBC, o Hibernate numera parâmetros a partir de zero. Parâmetros
nomeados são identificadores da forma:name na faixa de consulta. As vantagens de parâmetros
nomeados são:
• Parâmetros nomeados são insensíveis à ordem que eles ocorrem na faixa de consulta
• eles podem ocorrer em tempos múltiplos na mesma consulta
• eles são auto documentáveis
//positional parameter
Query q = sess.createQuery("from DomesticCat cat where cat.name = ?");
q.setString(0, "Izi");
222
Executando consultas
11.4.1.5. Paginação
Se você precisar especificar vínculos do conjunto de resultados, o máximo de números por linha
que quiser recuperar e/ou a primeira linha que quiser recuperar, você deve usar métodos de
interface Consulta:
O Hibernate sabe como traduzir esta consulta de limite para a SQL nativa de seu DBMS
Se seu driver JDBC driver suportar ResultSets roláveis, a interface da Consulta poderá ser
usada para obter um objeto de ScrollableResults, que permite uma navegação flexível dos
resultados de consulta.
// find the first name on each page of an alphabetical list of cats by name
firstNamesOfPages = new ArrayList();
do {
String name = cats.getString(0);
firstNamesOfPages.add(name);
}
while ( cats.scroll(PAGE_SIZE) );
223
Capítulo 11. Trabalhando com ...
cats.close()
Note que uma conexão aberta de banco de dados (e cursor) é requerida para esta função, use
setMaxResult()/setFirstResult() se precisar da função de paginação offline.
Queries can also be configured as so called named queries using annotations or Hibernate
mapping documents. @NamedQuery and @NamedQueries can be defined at the class level as seen
in Exemplo 11.1, “Defining a named query using @NamedQuery” . However their definitions are
global to the session factory/entity manager factory scope. A named query is defined by its name
and the actual query string.
@Entity
@NamedQuery(name="night.moreRecentThan", query="select n from Night n where n.date >= :date")
public class Night {
...
}
Using a mapping document can be configured using the <query> node. Remember to use a CDATA
section if your query contains characters that could be interpreted as markup.
<query name="ByNameAndMaximumWeight"><![CDATA[
from eg.DomesticCat as cat
where cat.name = ?
and cat.weight > ?
] ]></query>
Parameter binding and executing is done programatically as seen in Exemplo 11.3, “Parameter
binding of a named query”.
224
Filtrando coleções
Query q = sess.getNamedQuery("ByNameAndMaximumWeight");
q.setString(0, name);
q.setInt(1, minWeight);
List cats = q.list();
Note que o código de programa atual é independente da linguagem de consulta que é utilizada,
você também pode definir as consultas SQL nativas no metadado, ou migrar consultas existentes
para o Hibernate, colocando-os em arquivos de mapeamento.
A coleção retornada é considerada uma bolsa, e é a cópia da coleção dada. A coleção original não
é modificada. Ela é oposta à implicação do nome "filtro", mas é consistente com o comportamento
esperado.
Observe que os filtros não requerem uma cláusula from embora possam ter um, se requerido.
Os filtros não são limitados a retornar aos elementos de coleção.
Até mesmo um filtro vazio é útil, ex.: para carregar um subconjunto em uma coleção enorme:
225
Capítulo 11. Trabalhando com ...
.setFirstResult(0).setMaxResults(10)
.list();
The Criteria and the associated Example API are discussed in more detail in Capítulo 17,
Consultas por critérios.
SQL queries can contain named and positional parameters, just like Hibernate queries. More
information about native SQL queries in Hibernate can be found in Capítulo 18, SQL Nativo.
226
Modificando objetos desacoplados
capítulo. Não há necessidade de chamar um método em particular (como update(), que possui
um propósito diferente) para fazer modificações persistentes. Portanto, a forma mais direta de
atualizar o estado de um objeto é carregá-lo() e depois manipulá-lo diretamente, enquanto a
Sessão estiver aberta:
Algumas vezes, este modelo de programação é ineficiente, uma vez que ele requer ambos SQL
SELECT (para carregar um objeto) e um SQLUPDATE (para persistir seu estado atualizado) na
mesma sessão. Por isso, o Hibernate oferece uma abordagem alternativa, usando instâncias
desanexadas.
Use update() se você tiver certeza de que a sessão já não contém uma instância persistente
com o mesmo identificador, e merge() se você quiser mesclar suas modificações a qualquer
momento sem considerar o estado da sessão. Em outras palavras, update() é geralmente o
primeiro método que você chama em uma nova sessão, assegurando que o re-acoplamento de
suas instâncias seja a primeira operação executada.
227
Capítulo 11. Trabalhando com ...
The application should individually update() detached instances that are reachable from the given
detached instance only if it wants their state to be updated. This can be automated using transitive
persistence. See Seção 11.11, “Persistência Transitiva” for more information.
O método lock() também permite que um aplicativo re-associe um objeto com uma nova sessão.
No entanto, a instância desanexada não pode ser modificada.
//just reassociate:
sess.lock(fritz, LockMode.NONE);
//do a version check, then reassociate:
sess.lock(izi, LockMode.READ);
//do a version check, using SELECT ... FOR UPDATE, then reassociate:
sess.lock(pk, LockMode.UPGRADE);
Note que lock() pode ser usado com diversos LockModes, veja a documentação API e o capítulo
sobre manuseio de transações para maiores informações. Re-acoplamento não é o único caso
de uso para lock().
Other models for long units of work are discussed in Seção 13.3, “Controle de concorrência
otimista”.
O uso e semântica do saveOrUpdate() parecem ser confusos para novos usuários. A princípio,
enquanto você não tentar usar instâncias de uma sessão em outra nova sessão, não precisará
utilizar update(), saveOrUpdate(), ou merge(). Algumas aplicações inteiras nunca precisarão
utilizar estes métodos.
228
Apagando objetos persistentes
• se existir uma instância persistente com um mesmo identificador associado atualmente com a
sessão, copie o estado do objeto dado para a instância persistente.
• se não existir uma instância persistente atualmente associada com a sessão, tente carregá-la
a partir do banco de dados, ou crie uma nova instância persistente
• a instância persistente é retornada
• a instância dada não se torna associada com a sessão, ela permanece desanexada
sess.delete(cat);
Você poderá deletar objetos na ordem que desejar, sem risco de violação de restrição da chave
estrangeira. É possível violar uma restrição NOT NULL em uma coluna de chave estrangeira,
apagando objetos na ordem inversa, ex.: se apagar o pai, mas esquecer de apagar o filho.
229
Capítulo 11. Trabalhando com ...
O ReplicationMode determina como o replicate() irá lidar com conflitos em linhas existentes
no banco de dados:
O caso de uso para este recurso inclui dados de reconciliação em instâncias de banco de dados
diferentes, atualizando informações da configuração do sistema durante a atualização do produto,
retornando mudanças realizadas durante transações não ACID entre outras funções.
230
Persistência Transitiva
Uma exceção é que o objeto que utiliza a geração de ID native é inserido quando salvo.
Exceto quando você explicitamente limpar(), não há nenhuma garantia sobre quando a Sessão
executará as chamadas de JDBC, somente se sabe a ordem na qual elas são executadas.
No entanto, o Hibernate garante que a Query.list(..) nunca retornará dados antigos, nem
retornará dados errados.
It is possible to change the default behavior so that flush occurs less frequently. The FlushMode
class defines three different modes: only flush at commit time when the Hibernate Transaction
API is used, flush automatically using the explained routine, or never flush unless flush() is
called explicitly. The last mode is useful for long running units of work, where a Session is kept
open and disconnected for a long time (see Seção 13.3.2, “Sessão estendida e versionamento
automático”).
sess = sf.openSession();
Transaction tx = sess.beginTransaction();
sess.setFlushMode(FlushMode.COMMIT); // allow queries to return stale state
During flush, an exception might occur (e.g. if a DML operation violates a constraint). Since
handling exceptions involves some understanding of Hibernate's transactional behavior, we
discuss it in Capítulo 13, Transações e Concorrência .
231
Capítulo 11. Trabalhando com ...
salvos também, quando o pai é deletado, os filhos também serão deletados, etc. Isto funciona até
para operações como remoção de filho da coleção. O Hibernate irá detectar isto e como objetos
de valor não podem ter referências compartilhadas, irá deletar o filho do banco de dados.
Agora considere o mesmo cenário com objeto pai e filho sendo entidades, e não de valores
(ex.: categorias e ítens, ou cats pai e filho). As entidades possuem seus próprios ciclos de vida,
suportam referências compartilhadas (portanto, remover uma entidade da coleção não significa
que possa ter sido deletada), e não existe efeito cascata de estado, por padrão, a partir de uma
entidade para outras entidades associadas. O Hibernate não implementa persistência por alcance
por padrão.
Você pode até utilizar cascade="all" para especificar que todas as operações devem estar em
cascata junto à associação. O padrão cascade="none" especifica que nenhuma operação deve
estar em cascata.
In case you are using annotatons you probably have noticed the cascade attribute taking an
array of CascadeType as a value. The cascade concept in JPA is very is similar to the transitive
persistence and cascading of operations as described above, but with slightly different semantics
and cascading types:
232
Persistência Transitiva
Nota
A special cascade style, delete-orphan, applies only to one-to-many associations, and indicates
that the delete() operation should be applied to any child object that is removed from the
association. Using annotations there is no CascadeType.DELETE-ORPHAN equivalent. Instead
you can use the attribute orphanRemoval as seen in Exemplo 11.4, “@OneToMany with
orphanRemoval”. If an entity is removed from a @OneToMany collection or an associated entity is
dereferenced from a @OneToOne association, this associated entity can be marked for deletion if
orphanRemoval is set to true.
@Entity
public class Customer {
private Set<Order> orders;
@OneToMany(cascade=CascadeType.ALL, orphanRemoval=true)
public Set<Order> getOrders() { return orders; }
[...]
}
@Entity
public class Order { ... }
Recomendações:
233
Capítulo 11. Trabalhando com ...
Ao mapear uma associação (tanto uma associação de valor único como uma coleção) com casca
de="all", a associação é demarcada como um relacionamento de estilo parent/child onde salvar/
atualizar/deletar do pai, resulta em salvar/atualizar/deletar do(s) filho(s).
Furthermore, a mere reference to a child from a persistent parent will result in save/update of
the child. This metaphor is incomplete, however. A child which becomes unreferenced by its
parent is not automatically deleted, except in the case of a one-to-many association mapped with
cascade="delete-orphan". The precise semantics of cascading operations for a parent/child
relationship are as follows:
• Se um pai é passado para persist(), todos os filhos são passados para persist()
• Se um pai é passado para merge(), todos os filhos são passados para merge()
• Se um pai for passado para save(), update() ou saveOrUpdate(), todos os filhos passarão
para saveOrUpdate()
• Se um filho transiente ou desanexado se tornar referenciado pelo pai persistente, ele será
passado para saveOrUpdate()
• Se um pai for deletado, todos os filhos serão passados para delete()
• Se um filho for diferenciado pelo pai persistente, nada de especial acontece - a aplicação deve
explicitamente deletar o filho se necessário, a não ser que casca de="delete-orphan", nos
quais casos o filho "órfão" é deletado.
Finalmente, note que o cascateamento das operações podem ser aplicados a um grafo de objeto
em tempo de chamada ou em tempo de limpeza. Todas as operações, se habilitadas, são
colocadas em cascata para entidades associadas atingíveis quando a operação for executada.
No entanto, save-upate e delete-orphan são transitivas para todas as entidades associadas
atingíveis durante a limpeza da Sessão.
234
Usando metadados
235
236
Read-only entities
Importante
Hibernate's treatment of read-only entities may differ from what you may have
encountered elsewhere. Incorrect usage may cause unexpected results.
• Hibernate does not dirty-check the entity's simple properties or single-ended associations;
• Hibernate will not update the version of the read-only entity if only simple properties or single-
ended updatable associations are changed;
In some ways, Hibernate treats read-only entities the same as entities that are not read-only:
• Hibernate updates the version if the entity has a collection with changes that dirties the entity;
Even if an entity is not read-only, its collection association can be affected if it contains a read-
only entity.
For details about the affect of read-only entities on different property and association types, see
Seção 12.2, “Read-only affect on property type”.
For details about how to make entities read-only, see Seção 12.1, “Making persistent entities
read-only”
237
Capítulo 12. Read-only entities
• you can map an entity class as immutable; when an entity of an immutable class is made
persistent, Hibernate automatically makes it read-only. see Seção 12.1.1, “Entities of immutable
classes” for details
• you can change a default so that entities loaded into the session by Hibernate are automatically
made read-only; see Seção 12.1.2, “Loading persistent entities as read-only” for details
• you can make an HQL query or criteria read-only so that entities loaded when the query or
criteria executes, scrolls, or iterates, are automatically made read-only; see Seção 12.1.3,
“Loading read-only entities from an HQL query/criteria” for details
• you can make a persistent entity that is already in the in the session read-only; see Seção 12.1.4,
“Making a persistent entity read-only” for details
An entity of an immutable class can created and deleted the same as an entity of a mutable class.
Hibernate treats a persistent entity of an immutable class the same way as a read-only persistent
entity of a mutable class. The only exception is that Hibernate will not allow an entity of an
immutable class to be changed so it is not read-only.
Nota
To change the default behavior so Hibernate loads entity instances of mutable classes into the
session and automatically makes them read-only, call:
Session.setDefaultReadOnly( true );
To change the default back so entities loaded by Hibernate are not made read-only, call:
Session.setDefaultReadOnly( false );
238
Loading read-only entities from an HQL query/criteria
Session.isDefaultReadOnly();
• Session.load()
• Session.get()
• Session.merge()
• executing, scrolling, or iterating HQL queries and criteria; to override this setting for a particular
HQL query or criteria see Seção 12.1.3, “Loading read-only entities from an HQL query/criteria”
• persistent entities already in the session when the default was changed
• persistent entities that are refreshed via Session.refresh(); a refreshed persistent entity will only
be read-only if it was read-only before refreshing
Nota
If Session.isDefaultReadOnly() returns false (the default) when an HQL query or criteria executes,
then entities and proxies of mutable classes loaded by the query will not be read-only.
You can override this behavior so that entities and proxies loaded by an HQL query or criteria are
automatically made read-only.
Query.setReadOnly( true );
239
Capítulo 12. Read-only entities
Criteria.setReadOnly( true );
Entities and proxies that exist in the session before being returned by an HQL query or criteria
are not affected.
Uninitialized persistent collections returned by the query are not affected. Later, when
the collection is initialized, entities loaded into the session will be read-only if
Session.isDefaultReadOnly() returns true.
When it is not possible to load and initialize all necessary entities in a single query or criteria,
you can temporarily change the session default to load entities as read-only before the query is
executed. Then you can explicitly initialize proxies and collections before restoring the session
default.
setDefaultReadOnly( true );
Contract contract =
( Contract ) session.createQuery(
"from Contract where customerName = 'Sherman'" )
.uniqueResult();
Hibernate.initialize( contract.getPlan() );
Hibernate.initialize( contract.getVariations() );
Hibernate.initialize( contract.getNotes() );
setDefaultReadOnly( false );
...
tx.commit();
session.close();
If Session.isDefaultReadOnly() returns true, then you can use Query.setReadOnly( false ) and
Criteria.setReadOnly( false ) to override this session setting and load entities that are not read-
only.
Nota
240
Read-only affect on property type
Session.setReadOnly(entityOrProxy, true)
Session.setReadOnly(entityOrProxy, false)
Importante
To throw away non-flushed changes and make the persistent entity consistent with its database
representation, call:
session.refresh( entity );
To flush changes made before or while the entity was read-only and make the database
representation consistent with the current state of the persistent entity:
241
Capítulo 12. Read-only entities
* Behavior is different when the entity having the property/association is read-only, compared to
when it is not read-only.
When a persistent object is read-only, Hibernate does not dirty-check simple properties.
Hibernate will not synchronize simple property state changes to the database. If you have
automatic versioning, Hibernate will not increment the version if any simple properties change.
// contract.getCustomerName() is "Sherman"
contract.setCustomerName( "Yogi" );
242
Unidirectional associations
tx.commit();
tx = session.beginTransaction();
Hibernate treats unidirectional one-to-one and many-to-one associations in the same way when
the owning entity is read-only.
We use the term unidirectional single-ended association when referring to functionality that is
common to unidirectional one-to-one and many-to-one associations.
Hibernate does not dirty-check unidirectional single-ended associations when the owning entity
is read-only.
Nota
If automatic versioning is used, Hibernate will not increment the version due to local changes to
unidirectional single-ended associations.
In the following examples, Contract has a unidirectional many-to-one association with Plan.
Contract cascades save and update operations to the association.
The following shows that changing a read-only entity's many-to-one association reference to null
has no effect on the entity's database representation.
243
Capítulo 12. Read-only entities
contract.setPlan( null );
tx.commit();
tx.commit();
session.close();
The following shows that, even though an update to a read-only entity's many-to-one association
has no affect on the entity's database representation, flush still cascades the save-update
operation to the locally changed association.
tx.commit();
session.close();
The collection can contain entities that are read-only, as well as entities that are not read-only.
Entities can be added and removed from the collection; changes are flushed to the database.
If automatic versioning is used, Hibernate will update the version due to changes in the collection
if they dirty the owning entity.
244
Bidirectional associations
• updates that change the association reference to null or to refer to a different entity will not be
flushed to the database.
• If automatic versioning is used, Hibernate will not increment the version due to local changes
to the association.
Nota
When the owner is not read-only, Hibernate treats an association with a read-only entity the same
as when the association is with an entity that is not read-only.
• the one-to-many side uses a non-inverse collection that contains the read-only entity
• a read-only entity can only be removed from the collection by an orphan delete or by explicitly
deleting the entity.
Hibernate treats bidirectional many-to-many associations owned by a read-only entity the same
as when owned by an entity that is not read-only.
245
Capítulo 12. Read-only entities
The collection on either side of the association can contain entities that are read-only, as well as
entities that are not read-only.
Entities are added and removed from both sides of the collection; changes are flushed to the
database.
If automatic versioning is used, Hibernate will update the version due to changes in both sides of
the collection if they dirty the entity owning the respective collections.
246
Transações e Concorrência
O fator mais importante sobre o Hibernate e o controle de concorrência é que é muito fácil de ser
compreendido. O Hibernate usa diretamente conexões de JDBC e recursos de JTA sem adicionar
nenhum comportamento de bloqueio a mais. Recomendamos que você gaste algum tempo com
o JDBC, o ANSI e a especificação de isolamento de transação de seu sistema de gerência da
base de dados.
O Hibernate não bloqueia objetos na memória. Sua aplicação pode esperar o comportamento
tal qual definido de acordo com o nível de isolamento de suas transações de banco de dados.
Note que graças ao Session, que também é um cache de escopo de transação, o Hibernate
procura repetidamente por identificadores e consultas de entidade não consultas de relatórios
que retornam valores escalares.
Uma Session é um objeto de baixo custo de criação, não é threadsafe, deve ser usado uma vez,
para uma única requisição, uma conversação, uma única unidade do trabalho e então deve ser
descartado. Um Session não obterá um JDBC Connection, ou um Datasource, a menos que
necessite. Isto não consome nenhum recurso até ser usado.
Uma transação precisa ser o mais curta possível, para reduzir a disputa pelo bloqueio na base de
dados. Transações longas impedirão que sua aplicação escale a carga altamente concorrente.
Por isso, não é bom manter uma transação de base de dados aberta durante o tempo que o
usuário pensa, até que a unidade do trabalho esteja completa.
Qual é o escopo de uma unidade de trabalho? Pode uma única Session do Hibernate gerenciar
diversas transações ou este é um o relacionamento um-para-um dos escopos? Quando você
deve abrir e fechar uma Session e como você demarca os limites da transação? Estas questões
estão endereçadas nas seguintes seções.
247
Capítulo 13. Transações e Con...
of changes and the resolution of concurrency problems. ”[PoEAA] In other words, its a series of
operations we wish to carry out against the database together. Basically, it is a transaction, though
fulfilling a unit of work will often span multiple physical database transactions (see Seção 13.1.2,
“Longas conversações”). So really we are talking about a more abstract notion of a transaction.
The term "business transaction" is also sometimes used in lieu of unit of work.
Primeiro, não use o antipattern sessão-por-operação: isto é, não abra e feche uma Session para
cada simples chamada ao banco de dados em uma única thread. Naturalmente, o mesmo se
aplica às transações do banco de dados. As chamadas ao banco de dados em uma aplicação são
feitas usando uma seqüência planejada, elas são agrupadas em unidades de trabalho atômicas.
Veja que isso também significa que realizar um auto-commit depois de cada instrução SQL é
inútil em uma aplicação, esta modalidade é ideal para o trabalho ad hoc do console do SQL. O
Hibernate impede, ou espera que o servidor de aplicação impessa isso, aplique a modalidade
auto-commit imediatamente. As transações de banco de dados nunca são opcionais, toda a
comunicação com um banco de dados tem que ocorrer dentro de uma transação, não importa se
você vai ler ou escrever dados. Como explicado, o comportamento auto-commit para leitura de
dados deve ser evitado, uma vez que muitas transações pequenas são improváveis de executar
melhor do que uma unidade de trabalho claramente definida. A última opção é também muito
mais sustentável e expandida.
Your application code can access a "current session" to process the request by calling
sessionFactory.getCurrentSession(). You will always get a Session scoped to the current
database transaction. This has to be configured for either resource-local or JTA environments,
see Seção 2.3, “Sessões Contextuais”.
248
Longas conversações
utilizam uma fase de renderização separada depois da requisição ter sido processada. Estender
a transação até que a renderização da visão esteja completa é fácil de fazer se você implementar
seu próprio interceptador. Entretanto, não será fácil se você confiar em EJBs com transações
gerenciadas por recipiente, porque uma transação será terminada quando um método de EJB
retornar, antes que a renderização de toda visão possa começar. Veja o website e o fórum do
Hibernate para dicas e exemplos em torno deste modelo de Sessão Aberta na Visualização.
• A primeira tela de um diálogo se abre e os dados vistos pelo usuário são carregados em uma
Session e transação de banco de dados particulares. O usuário está livre para modificar os
objetos.
• O usuário clica em "Salvar" após 5 minutos e espera suas modificações serem persistidas. O
usuário também espera que ele seja a única pessoa que edita esta informação e que nenhuma
modificação conflitante possa ocorrer.
Nós chamamos esta unidade de trabalho, do ponto da visão do usuário, uma conversação de
longa duração (ou transação da aplicação). Há muitas maneiras de você implementar em sua
aplicação.
Uma primeira implementação simples pode manter a Session e a transação aberta durante o
tempo de interação do usuário, com bloqueios na base de dados para impedir a modificação
concorrente e para garantir o isolamento e a atomicidade. Esse é naturalmente um anti-pattern,
uma vez que a disputa do bloqueio não permitiria o escalonameneto da aplicação com o número
de usuários concorrentes.
Claramente, temos que usar diversas transações para implementar a conversação. Neste caso,
manter o isolamento dos processos de negócio, torna-se responsabilidade parcial da camada
da aplicação. Uma única conversação geralmente usa diversas transações. Ela será atômica
se somente uma destas transações (a última) armazenar os dados atualizados, todas as outras
simplesmente leram os dados (por exemplo em um diálogo do estilo wizard que mede diversos
ciclos de requisição/resposta). Isto é mais fácil de implementar do parece, especialmente se você
usar as características do Hibernate:
249
Capítulo 13. Transações e Con...
usuário estiver pensando. O Hibernate permite que você re-anexe os objetos e persista
as modificações, esse pattern é chamado sessão-por-solicitação-com-objetos-desanexados.
Utiliza-se versionamento automático para isolar as modificações concorrentes.
• Sessão Estendida (ou Longa) A Session do Hibernate pode ser desligada da conexão
adjacente do JDBC depois que a transação foi submetida, e ser reconectada quando uma
nova requisição do cliente ocorrer. Este pattern é conhecido como sessão-por-conversação
e faz o reatamento uniforme desnecessário. Versionamento automático é usado para
isolar modificações concorrentes e a sessão-por-conversação geralmente pode ser nivelada
automaticamente, e sim explicitamente.
Identidade da JVM
foo==bar
Então para os objetos acoplados a uma Session específica (ex.: isto está no escopo de
uma Session), as duas noções são equivalentes e a identidade da JVM para a identidade
da base de dados é garantida pelo Hibernate. Entretanto, embora a aplicação possa acessar
concorrentemente o "mesmo" objeto do negócio (identidade persistente) em duas sessões
diferentes, as duas instâncias serão realmente "diferentes" (identidade de JVM). Os conflitos
são resolvidos usando (versionamento automático) no flush/commit, usando uma abordagem
otimista.
No entanto, uma aplicação que usa == fora de uma Session, pode ver resultados inesperados.
Isto pode ocorrer mesmo em alguns lugares inesperados, por exemplo, se você colocar duas
instâncias desacopladas em um mesmo Set. Ambas podem ter a mesma identidade na base de
dados (ex.: elas representam a mesma linha), mas a identidade da JVM não é, por definição,
garantida para instâncias em estado desacoplado. O desenvolvedor tem que substituir os
métodos equals() e hashCode() em classes persistentes e implementar sua própria noção da
250
Edições comuns
igualdade do objeto. Advertência: nunca use o identificador da base de dados para implementar a
igualdade, use atributos de negócio, uma combinação única, geralmente imutável. O identificador
da base de dados mudará se um objeto transiente passar para o estado persistente. Se a instância
transiente (geralmente junto com instâncias desacopladas) for inserida em um Set, a mudança
do hashcode quebrará o contrato do Set. As funções para chaves de negócio não têm que
ser tão estável quanto às chaves primárias da base de dados, você somente tem que garantir
a estabilidade durante o tempo que os objetos estiverem no mesmo Set. Veja o website do
Hibernate para uma discussão mais completa sobre o assunto. Note também que esta não é uma
característica do Hibernate, mas simplesmente a maneira como a identidade e a igualdade do
objeto de Java têm que ser implementadas.
• Uma Session não é threadsafe. As coisas que são supostas para trabalhar concorrentemente,
como requisições HTTP, beans de sessão, ou Swing, causarão condições de disputa se uma
instância Session for compartilhada. Se você mantiver sua Session do Hibernate em seu
HttpSession (discutido mais tarde), você deverá considerar sincronizar o acesso a sua sessão
do HTTP. Caso contrário, um usuário que clica em recarga rápido demais, pode usar o mesmo
Session em duas threads executando simultaneamente.
• Uma exceção lançada pelo Hibernate significa que você tem que dar rollback na sua transação
no banco de dados e fechar a Session imediatamente (discutido mais tarde em maiores
detalhes). Se sua Session é limitada pela aplicação, você tem que parar a aplicação. Fazer o
rollback na transação no banco de dados não retorna seus objetos do negócio ao estado que
estavam no início da transação. Isto significa que o estado da base de dados e os objetos de
negócio perdem a sincronização. Geralmente, não é um problema porque as exceções não
são recuperáveis e você tem que iniciar após o rollback de qualquer maneira.
• The Session caches every object that is in a persistent state (watched and checked for dirty
state by Hibernate). If you keep it open for a long time or simply load too much data, it will
grow endlessly until you get an OutOfMemoryException. One solution is to call clear() and
evict() to manage the Session cache, but you should consider a Stored Procedure if you need
mass data operations. Some solutions are shown in Capítulo 15, Batch processing. Keeping a
Session open for the duration of a user session also means a higher probability of stale data.
251
Capítulo 13. Transações e Con...
Uma aplicação do Hibernate pode funcionar em ambientes não gerenciados (isto é, aplicações
standalone, Web simples ou Swing) e ambientes gerenciados J2EE. Em um ambiente não
gerenciado, o Hibernate é geralmente responsável pelo seu próprio pool de conexões. O
desenvolvedor, precisa ajustar manualmente os limites das transaçãos, ou seja, começar,
submeter ou efetar rollback nas transações ele mesmo. Um ambiente gerenciado fornece
transações gerenciadas por recipiente (CMT), com um conjunto da transações definido
declarativamente em descritores de implementação de beans de sessão EJB, por exemplo. A
demarcação programática é portanto, não mais necessária.
• liberar a sessão
• submeter a transação
• fechar a sessão
• tratar as exceções
A liberação da sessão já foi bem discutida, agora nós daremos uma olhada na demarcação da
transação e na manipulação de exceção em ambientes controlados e não controlados.
// do some work
...
tx.commit();
252
Usando JTA
}
catch (RuntimeException e) {
if (tx != null) tx.rollback();
throw e; // or display error message
}
finally {
sess.close();
}
Você não pode chamar flush() da Session() explicitamente. A chamada ao commit() dispara
automaticamente a sincronização para a sessão, dependendo do Seção 11.10, “Limpando a
Sessão”. Uma chamada ao close() marca o fim de uma sessão. A principal implicação do
close() é que a conexão JDBC será abandonada pela sessão. Este código Java é portável e
funciona em ambientes não gerenciados e de JTA.
Uma solução muito mais flexível é o gerenciamento de contexto "sessão atual" da construção
interna do Hibernate, como descrito anteriormente:
// do some work
...
factory.getCurrentSession().getTransaction().commit();
}
catch (RuntimeException e) {
factory.getCurrentSession().getTransaction().rollback();
throw e; // or display error message
}
Você muito provavelmente nunca verá estes fragmentos de código em uma aplicação regular; as
exceções fatais (do sistema) devem sempre ser pegas no "topo". Ou seja, o código que executa
chamadas do Hibernate (na camada de persistência) e o código que trata RuntimeException
(e geralmente pode somente limpar acima e na saída) estão em camadas diferentes. O
gerenciamento do contexto atual feito pelo Hibernate pode significativamente simplificar este
projeto, como tudo que você necessita é do acesso a um SessionFactory. A manipulação de
exceção é discutida mais tarde neste capítulo.
253
Capítulo 13. Transações e Con...
standalone de JTA e usá-la sem EJB. O Hibernate oferece duas estratégias para a integração
de JTA.
Se você usar transações de bean gerenciado (BMT) o Hibernate dirá ao servidor de aplicação
para começar e para terminar uma transação de BMT se você usar a Transaction API. Assim,
o código de gerência de transação é idêntico ao ambiente não gerenciado.
// BMT idiom
Session sess = factory.openSession();
Transaction tx = null;
try {
tx = sess.beginTransaction();
// do some work
...
tx.commit();
}
catch (RuntimeException e) {
if (tx != null) tx.rollback();
throw e; // or display error message
}
finally {
sess.close();
}
Se você quiser usar uma Session limitada por transação, isto é, a funcionalidade do
getCurrentSession() para a propagação fácil do contexto, você terá que usar diretamente a
API JTA UserTransaction:
tx.begin();
tx.commit();
}
catch (RuntimeException e) {
tx.rollback();
throw e; // or display error message
}
254
Tratamento de Exceção
// CMT idiom
Session sess = factory.getCurrentSession();
// do some work
...
Em um CMT/EJB, até mesmo um rollback acontece automaticamente, desde que uma exceção
RuntimeException não tratável seja lançada por um método de um bean de sessão que informa
ao recipiente ajustar a transação global ao rollback. Isto significa que você não precisa mesmo
usar a API Transaction do Hibernate com BMT ou CMT e você obterá a propagação automática
da Sessão "atual" limitada à transação.
A exceção HibernateException, a qual envolve a maioria dos erros que podem ocorrer em
uma camada de persistência do Hibernate, é uma exceção não verificada. Ela não constava em
versões mais antigas de Hibernate. Em nossa opinião, nós não devemos forçar o desenvolvedor
a tratar uma exceção irrecuperável em uma camada mais baixa. Na maioria dos sistemas, as
exceções não verificadas e fatais são tratadas em um dos primeiros frames da pilha da chamada
do método (isto é, em umas camadas mais elevadas) e uma mensagem de erro é apresentada ao
usuário da aplicação (ou alguma outra ação apropriada é feita). Note que Hibernate pode também
lançar outras exceções não verificadas que não sejam um HibernateException. Estas, também
são, irrecuperáveis e uma ação apropriada deve ser tomada.
255
Capítulo 13. Transações e Con...
// do some work
...
sess.getTransaction().commit()
}
catch (RuntimeException e) {
sess.getTransaction().rollback();
throw e; // or display error message
}
finally {
sess.close();
256
Controle de concorrência otimista
Veja que setTimeout() não pode ser chamado em um bean CMT, onde o tempo de espera das
transações deve ser definido declaradamente.
Em uma implementação sem muita ajuda do Hibernate, cada interação com o banco de dados
ocorre em uma nova Session e o desenvolvedor é responsável por recarregar todas as instâncias
persistentes da base de dados antes de manipulá-las. Este caminho força a aplicação a realizar
sua própria checagem de versão para assegurar a conversação do isolamento da transação.
Este caminho é menos eficiente em termos de acesso ao banco de dados. É o caminho mais
similar à entidade EJBs.
t.commit();
session.close();
Claro, se você estiver operando em um ambiente de baixa concorrência de dados e não precisar
da checagem de versão, você pode usar este caminho e apenas pular a checagem de versão.
Nesse caso, o último commit realizado é a estratégia padrão para suas conversações longas.
Tenha em mente que isto pode confundir os usuários da aplicação, como também poderão
ter atualizações perdidas sem mensagens de erro ou uma possibilidade de ajustar mudanças
conflitantes.
257
Capítulo 13. Transações e Con...
Claro que, a checagem manual da versão é somente possível em circunstâncias triviais e não
para a maioria de aplicações. Freqüentemente, os gráficoscompletos de objetos modificados
têm que ser verificados, não somente únicas instâncias. O Hibernate oferece checagem de
versão automática com uma Session estendida ou instâncias desatachadas como o paradigma
do projeto.
Uma única instância de Session e suas instâncias persistentes são usadas para a conversação
inteira, isto é conhecido como sessão-por-conversação. O Hibernate verifica versões da instância
no momento da liberação, lançando uma exceção se a modificação concorrente for detectada. Até
o desenvolvedor pegar e tratar essa exceção. As opções comuns são a oportunidade para que
o usuário intercale as mudanças ou reinicie a conversação do negócio com dados não antigos.
foo.setProperty("bar");
O objeto foo sabe que a Session já foi carregada. Ao começar uma nova transação ou uma
sessão velha, você obterá uma conexão nova e reiniciará a sessão. Submeter uma transação
implica em desconectar uma sessão da conexão JDBC e retornar à conexão ao pool. Após a
reconexão, para forçar uma checagem de versão em dados que você não esteja atualizando, você
poderá chamar Session.lock() com o LockMode.READ em todos os objetos que possam ter sido
atualizados por uma outra transação. Você não precisa bloquear nenhum dado que você está
atualizando. Geralmente, você configuraria FlushMode.NEVER em uma Session estendida, de
modo que somente o último ciclo da transação tenha permissão de persistir todas as modificações
feitas nesta conversação. Por isso, somente esta última transação incluiria a operação flush()
e então também iria close() a sessão para terminar a conversação.
Este modelo é problemático se a Session for demasiadamente grande para ser armazenada
durante o tempo de espera do usuário (por exemplo uma HttpSession deve ser mantida o menor
possível). Como a Session é também cache de primeiro nível (imperativo) e contém todos os
objetos carregados, nós podemos provavelmente usar esta estratégia somente para alguns ciclos
de requisição/resposta. Você deve usar a Session somente para uma única conversação, porque
ela logo também estará com dados velhos.
258
Objetos destacados e versionamento automático
Nota
Note que versões mais atuais de Hibernate requerem a desconexão e reconexão
explícitas de uma Session. Estes métodos são desatualizados, pois o início e
término de uma transação têm o mesmo efeito.
Note também que você deve manter a Session desconectada, fechada para a camada de
persistência. Ou seja, use um bean de sessão com estado EJB para prender a Session em um
ambiente de três camadas. Não transfira à camada web, ou até serializá-lo para uma camada
separada, para armazená-lo no HttpSession.
Outra vez, o Hibernate verificará versões da instância durante a liberação, lançando uma exceção
se ocorrer conflitos de atualizações.
Você pode também chamar o lock() em vez de update() e usar LockMode.READ (executando
uma checagem de versão, ignorando todos os caches) se você estiver certo de que o objeto não
foi modificado.
Os esquemas da base de dados legado são freqüentemente estáticos e não podem ser
modificados. Ou então, outras aplicações puderam também acessar a mesma base de dados
259
Capítulo 13. Transações e Con...
e não sabem tratar a versão dos números ou carimbos de hora. Em ambos os casos, o
versionamento não pode confiar em uma coluna particular em uma tabela. Para forçar uma
checagem de versão sem uma versão ou mapeamento da propriedade do carimbo de hora
com uma comparação do estado de todos os campos em uma linha, configure optimistic-
lock="all" no mapeamento <class>. Note que isto conceitualmente é somente feito em
trabalhos se o Hibernate puder comparar o estado velho e novo (ex.: se você usar uma única
Session longa e não uma sessão-por-solicitação-com-objetos-desanexados).
Às vezes a modificação concorrente pode ser permitida, desde que as mudanças realizadas
não se sobreponham. Se você configurar optimistic-lock="dirty" ao mapear o <class>, o
Hibernate comparará somente campos modificados durante a liberação.
O Hibernate usará sempre o mecanismo de bloqueio da base de dados, nunca bloquiar objetos
na memória.
A classe LockMode define os diferentes níveis de bloqueio que o Hibernate pode adquirir. Um
bloqueio é obtido pelos seguintes mecanismos:
260
Modos para liberar a conexão
• LockMode.NONE representa a ausência do bloqueio. Todos os objetos mudam para esse estado
de bloqueio no final da Transaction. Objetos associados com a sessão através do método
update() ou saveOrUpdate() também são inicializados com esse modo de bloqueio.
Se o banco de dados não suportar o modo de bloqueio solicitado, o Hibernate usará um modo
alternativo apropriado, ao invés de lançar uma exceção. Isso garante que a aplicação seja portátil.
• ON_CLOSE: é o modo legado descrito acima. A sessão do Hibernate obtém a conexão quando
precisar executar alguma operação JDBC pela primeira vez e mantém enquanto a conexão
não for fechada.
• AFTER_TRANSACTION: informa que a conexão deve ser liberada após a conclusão de uma
org.hibernate.Transaction.
• AFTER_STATEMENT (também conhecida como liberação agressiva): informa que a conexão
deve ser liberada após a execução de cada instrução. A liberação agressiva não ocorre se a
instrução deixa pra trás algum recurso aberto associado com a sessão obtida. Atualmente, a
única situação em que isto ocorre é com o uso de org.hibernate.ScrollableResults.
261
Capítulo 13. Transações e Con...
262
Interceptadores e Eventos
É muito útil quando a aplicação precisa reagir a certos eventos que ocorrem dentro do Hibernate.
Isso permite a implementação de certas funções genéricas, assim como permite estender as
funcionalidades do Hibernate.
14.1. Interceptadores
A interface Interceptor permite fornecer informações da sessão para o aplicativo, permitindo
que o aplicativo inspecione e/ou manipule as propriedades de um objeto persistente antes de ser
salvo, atualizado, excluído ou salvo. Pode ser usado para gerar informações de auditoria. Por
exemplo, o seguinte Interceptor ajusta a função automaticamente createTimestamp quando
um Auditable é criado e atualiza a função lastUpdateTimestamp quando um Auditable é
atualizado.
package org.hibernate.test;
import java.io.Serializable;
import java.util.Date;
import java.util.Iterator;
import org.hibernate.EmptyInterceptor;
import org.hibernate.Transaction;
import org.hibernate.type.Type;
263
Capítulo 14. Interceptadores ...
264
Sistema de Eventos
Para todos os efeitos esses listeners devem ser considerados singletons. Isto significa que eles
são compartilhados entre as requisições, e assim sendo, não devem salvar nenhum estado das
variáveis instanciadas.
265
Capítulo 14. Interceptadores ...
}
}
}
Você também precisa adicionar uma entrada no XML de configuração do Hibernate para registrar
declarativamente qual listener deve se utilizado em conjunto com o listener padrão:
<hibernate-configuration>
<session-factory>
...
<event type="load">
<listener class="com.eg.MyLoadListener"/>
<listener class="org.hibernate.event.def.DefaultLoadEventListener"/>
</event>
</session-factory>
</hibernate-configuration
>
Mas, por quê implementar uma interface e definir o tipo específico durante a configuração? Bem,
um listener pode implementar vários listeners de evento. Com o tipo sendo definido durante o
registro, fica fácil ligar ou desligar listeners personalizados durante a configuração.
Primeiro, você precisa configurar um evento listener apropriado, para possibilitar o uso da
autorização JAAS.
266
Segurança declarativa do Hibernate
Os nomes das funções são as funções conhecidas pelo seu provedor JACC.
267
268
Batch processing
Uma alternativa para inserir 100.000 linhas no banco de dados usando o Hibernate pode ser a
seguinte:
Isto irá falhar com um OutOfMemoryException em algum lugar próximo a linha 50.000. Isso ocorre
devido ao fato do Hibernate fazer cache de todas as instâncias de Customer inseridas num cachê
em nível de sessão. Nós demonstraremos neste capitulo como evitar este problema.
Entretanto, se você vai realizar processamento em lotes, é muito importante que você habilite o
uso de lotes JDBC, se você pretende obter um desempenho razoável. Defina o tamanho do lote
JDBC em um valor razoável (algo entre 10-50, por exemplo):
hibernate.jdbc.batch_size 20
Note que o Hibernate desabilita o loteamento de inserção no nível JDBC de forma transparente
se você utilizar um gerador de identificador identiy.
Você também pode querer rodar esse tipo de processamento em lotes com o cache secundário
completamente desabilitado:
hibernate.cache.use_second_level_cache false
Mas isto não é absolutamente necessário, desde que possamos ajustar o CacheMode para
desabilitar a interação com o cache secundário.
269
Capítulo 15. Batch processing
tx.commit();
session.close();
tx.commit();
session.close();
270
Operações no estilo DML
devido à falta do cachê primário. Uma Sessão sem Estado é uma abstração de baixo nível, muito
mais próxima do JDBC adjacente.
tx.commit();
session.close();
Veja neste exempo, as instâncias de Customer retornadas pela consulta, são imediatamente
desvinculadas. Elas nunca serão associadas à um contexto persistente.
271
Capítulo 15. Batch processing
• Nenhum joins, tanto implícito ou explícito, pode ser especificado em uma consulta de volume
HQL. As Sub-consultas podem ser utilizadas na cláusula onde, em que as subconsultas podem
conter uniões.
• A clausula onde também é opcional.
String hqlUpdate = "update Customer c set c.name = :newName where c.name = :oldName";
// or String hqlUpdate = "update Customer set name = :newName where name = :oldName";
int updatedEntities = s.createQuery( hqlUpdate )
.setString( "newName", newName )
.setString( "oldName", oldName )
.executeUpdate();
tx.commit();
session.close();
As instruções do HQL UPDATE por padrão não afetam o version ou os valores de propriedade
timestamp para as entidades afetadas, de acordo com a especificação EJB3. No entanto,
você poderá forçar o Hibernate a redefinir corretamente os valores de propriedade version ou
timestamp usando um versioned update. Para tal, adicione uma palavra chave VERSIONED
após a palavra chave UPDATE.
272
Operações no estilo DML
.executeUpdate();
tx.commit();
session.close();
• Apenas a forma INSERT INTO ... SELECT ... é suportada; INSERT INTO ... VALUES ... não
é suportada.
273
Capítulo 15. Batch processing
a partir da instrução select correspondente, ou ele pode ser omitido da properties_list (neste
caso utiliza-se o seed value definido pela classe org.hibernate.type.VersionType).
String hqlInsert = "insert into DelinquentAccount (id, name) select c.id, c.name from Customer
c where ...";
int createdEntities = s.createQuery( hqlInsert )
.executeUpdate();
tx.commit();
session.close();
274
HQL: A Linguagem de Consultas do
Hibernate
O Hibernate vem com uma poderosa linguagem de consulta (HQL) que é muito parecida com o
SQL. No entanto, comparado com o SQL o HQL é totalmente orientado à objetos, e compreende
noções de herança, polimorfismo e associações.
Esse manual usa as palavras chave HQL em letras minúsculas. Alguns usuários acreditam que
com letras maiúsculas as consultas ficam mais legíveis, mas nós acreditamos que este formato
não é apropriado para o código Java.
from eg.Cat
Isto simplesmente retornará todas as instâncias da classe eg.Cat. Geralmente não precisamos
qualificar o nome da classe, uma vez que o auto-import é o padrão. Por exemplo:
from Cat
Com o objetivo de referir-se ao Cat em outras partes da consulta, você precisará determinar um
alias. Por exemplo:
Essa consulta atribui um alias a cat para as instâncias de Cat, portanto poderemos usar esse
alias mais tarde na consulta. A palavra chave as é opcional. Você também pode escrever assim:
275
Capítulo 16. HQL: A Linguagem...
É considerada uma boa prática nomear alias de consulta, utilizando uma letra minúscula inicial,
consistente com os padrões de nomeação Java para variáveis locais (ex.: domesticCat).
• inner join
• left outer join
• right outer join
• união completa (geralmente não é útil)
As construções inteiro, união esquerda externa e união direita externa podem ser
abreviadas.
Você pode fornecer condições extras de união usando a palavra chave do HQL with.
276
Associações e uniões
A "fetch" join allows associations or collections of values to be initialized along with their parent
objects using a single select. This is particularly useful in the case of a collection. It effectively
overrides the outer join and lazy declarations of the mapping file for associations and collections.
See Seção 21.1, “Estratégias de Busca ” for more information.
Geralmente, uma união de busca não precisa atribuir um alias, pois o objeto associado não deve
ser usado na cláusula where (ou em qualquer outra cláusula). Também, os objetos associados
não são retornados diretamente nos resultados da consulta. Ao invés disso, eles devem ser
acessados usando o objeto pai. A única razão pela qual precisariamos de um alias é quando
fazemos uma união de busca recursivamente em uma coleção adicional:
Observe que a construção busca não deve ser usada em consultas invocadas usando iterate()
(embora possa ser usado com scroll()). O Fetch também não deve ser usado junto com
o setMaxResults() ou setFirstResult() pois essas operações são baseadas nas linhas
retornadas, que normalmente contém duplicidade devido à busca das coleções, então o número
de linhas pode não ser o que você espera. A Fetch não deve ser usada junto com uma condição
with. É possível que seja criado um produto cartesiano pela busca de união em mais do que uma
coleção em uma consulta, então tome cuidado nesses casos. Uma busca de união em várias
coleções pode trazer resultados inesperados para mapeamentos do tipo bag, tome cuidado na
hora de formular consultas como essas. Finalmente, observe o seguinte, a busca de união
completa e busca de união direita não são importantes.
277
Capítulo 16. HQL: A Linguagem...
from Document doc fetch all properties where lower(doc.name) like '%cats%'
As consultas apresentadas na seção anterior usam a forma explícita, onde a palavra chave
união é explicitamente usada na cláusula from. Essa é a forma recomendada.
A forma implícita não usa a palavra chave "união". Entretanto, as associações são
"diferenciadas" usando pontuação ("." - dot-notation). Uniõesimplícitas podem aparecer em
qualquer uma das cláusulas HQL. A união implícita resulta em declarações SQL que contém
uniões inteiras.
• A propriedade especial (em letra minúscula) id pode ser usada para se referir à propriedade
do identificador de uma entidade considerando que a entidade não define uma propriedade
não identificadora chamada id.
• Se a entidade definir a propriedade do identificador nomeada, você poderá usar este nome
de propriedade.
Importante
278
A cláusula select
select mate
from Cat as cat
inner join cat.mate as mate
As consultas podem retornar múltiplos objetos e/ou propriedades como uma matriz do tipo
Object[]:
Ou como um List:
Ou - considerando que a classe Family tenha um construtor apropriado - como um objeto Java
typesafe atual:
279
Capítulo 16. HQL: A Linguagem...
Isto é bem mais útil quando usado junto comselecione novo mapa:
As palavras distinct e all podem ser usadas e têm a mesma semântica que no SQL.
280
Pesquisas Polimórficas
retorna instâncias não só de Cat, mas também de subclasses como DomesticCat. As consultas
do Hibernate podem nomear qualquer classe Java ou interface na cláusula from. A consulta
retornará instâncias de todas as classes persistentes que extendam a determinada classe ou
implemente a determinada interface. A consulta a seguir, poderia retornar todos os objetos
persistentes:
from java.lang.Object o
Note que as duas últimas consultas requerem mais de um SQL SELECT. Isto significa que a
cláusula order by não ordena corretamente todo o resultado. Isso também significa que você
não pode chamar essas consultas usando consulta.scroll().
281
Capítulo 16. HQL: A Linguagem...
A seguinte consulta:
select foo
from Foo foo, Bar bar
where foo.startDate = bar.date
retornará todas as instâncias de Foo, para cada um que tiver uma instância de bar com a
propriedade date igual a propriedade startDate de Foo. Expressões de caminho compostas
fazem da cláusula where, extremamente poderosa. Consideremos:
Esta consulta traduz para uma consulta SQL com uma tabela (inner) união. Por exemplo:
O operador = pode ser usado para comparar não apenas propriedades, mas também instâncias:
The special property (lowercase) id can be used to reference the unique identifier of an object.
See Seção 16.5, “Referência à propriedade do identificador ” for more information.
282
Expressões
Mais uma vez, a segunda consulta não precisa de nenhuma união de tabela.
See Seção 16.5, “Referência à propriedade do identificador ” for more information regarding
referencing identifier properties)
You can also use components or composite user types, or properties of said component types.
See Seção 16.17, “Componentes” for more information.
Um tipo "any" possui as propriedades id e class especiais, nos permitindo expressar uma união
da seguinte forma (onde AuditLog.item é uma propriedade mapeada com<any>):
16.10. Expressões
As expressões permitidas na cláusula where incluem o seguinte:
• operadores matemáticos: +, -, *, /
• operadores de comparação binários: =, >=, <=, <>, !=, like
• operadores lógicos and, or, not
• Parênteses ( ) que indica o agrupamento
• in, not in, between, is null, is not null, is empty, is not empty, member of and
not member of
• case "simples" , case ... when ... then ... else ... end, and "searched" case, case
when ... then ... else ... end
283
Capítulo 16. HQL: A Linguagem...
from DomesticCat cat where cat.name not between 'A' and 'B'
Da mesma forma, is null e is not null podem ser usados para testar valores nulos.
<property name="hibernate.query.substitutions"
>true 1, false 0</property
>
284
Expressões
Isso irá substituir as palavras chave true e falsepelos literais 1 e 0 na tradução do HQL para
SQL.
Pode-se testar o tamanho de uma coleção com a propriedade especial size ou a função especial
size().
Para coleções indexadas, você pode se referir aos índices máximo e mínimo, usando as funções
minindex e maxindex. Igualmente, pode-se referir aos elementos máximo e mínimo de uma
coleção de tipos básicos usando as funções minelement e maxelement. Por exemplo:
As funções SQL any, some, all, exists, in são suportadas quando passado o elemento
ou o conjunto de índices de uma coleção (elements e índices de funções) ou o resultado de
uma subconsulta (veja abaixo):
285
Capítulo 16. HQL: A Linguagem...
Note que essas construções - tamanho, elementos, índices, minindex, maxindex, minelement,
maxelement – só podem ser usados na cláusula where do Hibernate3.
Elementos de coleções com índice (matriz, listas, mapas) podem ser referenciadas pelo índice
(apenas na cláusula where):
O HQL também provê a função interna index() para elementos de associação um-para-muitos
ou coleção de valores.
286
A cláusula ordenar por
Funções escalares SQL, suportadas pelo banco de dados subjacente podem ser usadas:
Se ainda não estiver totalmente convencido, pense o quão maior e menos legível poderia ser a
consulta a seguir, em SQL:
select cust
from Product prod,
Store store
inner join store.customers cust
where prod.name = 'widget'
and store.location.name in ( 'Melbourne', 'Sydney' )
and prod = all elements(cust.currentOrder.lineItems)
287
Capítulo 16. HQL: A Linguagem...
Funções SQL e funções agregadas são permitidas nas cláusulas having e order by, se
suportadas pelo banco de dados subjacentes (ex: não no MeuSQL).
select cat
from Cat cat
join cat.kittens kitten
group by cat.id, cat.name, cat.other, cat.properties
having avg(kitten.weight)
> 100
order by count(kitten) asc, sum(kitten.weight) desc
Note que, nem a cláusula group by ou order by podem conter expressões aritméticas. O
Hibernate também não expande atualmente uma entidade agrupada, portanto você não pode
escrever group by cat caso todas as propriedades do cat não estiverem agregadas. Você
precisa listar claramente todas as propriedades não-agregadas.
16.13. Subconsultas
Para bancos de dados que suportam subseleções, o Hibernate suporta subconsultas dentro
de consultas. Uma subconsulta precisa estar entre parênteses (normalmente uma chamada
de função agregada SQL). Mesmo subconsultas co-relacionadas (subconsultas que fazem
referência à alias de outras consultas), são aceitas.
288
Exemplos de HQL
Note que HQL subconsultas podem aparecer apenas dentro de cláusulas select ou where.
Note that subqueries can also utilize row value constructor syntax. See Seção 16.18, “Sintáxe
do construtor de valores de linha” for more information.
A consulta a seguir retorna o id de ordenar, número de ítens e o valor total do ordenar para
todos os ordenar não pagos para um cliente particular e valor total mínimo dado, ordenando os
resultados por valor total. Para determinar os preços, utiliza-se o catálogo atual. A consulta SQL
resultante, usando tabelas ORDER, ORDER_LINE, PRODUCT, CATALOG e PRICE, têm quatro uniões
inteiras e uma subseleção (não correlacionada).
289
Capítulo 16. HQL: A Linguagem...
Que monstro! Na verdade, na vida real, eu não sou muito afeiçoado à subconsultas, então minha
consulta seria mais parecida com isto:
290
Exemplos de HQL
statusChange.timeStamp = (
select max(change.timeStamp)
from PaymentStatusChange change
where change.payment = payment
)
and statusChange.user <
> :currentUser
)
group by status.name, status.sortOrder
order by status.sortOrder
A próxima consulta usa a função isNull() do Servidor MS SQL, para retornar todas as contas
e pagamentos não efetuados para a organização, para aquele que o usuário atual pertencer.
Traduz-se para uma consulta SQL com três uniões inteiras, uma união externa e uma subseleção
nas tabelas ACCOUNT, PAYMENT, PAYMENT_STATUS, ACCOUNT_TYPE, ORGANIZATION e ORG_USER .
291
Capítulo 16. HQL: A Linguagem...
Para ordenar um resultado pelo tamanho de uma coleção, use a consulta a seguir.
Se seu banco de dados suporta subseleções, pode-se colocar uma condição sobre tamanho de
seleção na cláusula where da sua consulta:
Com essa solução não se pode retornar um User sem nenhuma menssagem, por causa da união
inteira, a forma a seguir também é útil:
292
Componentes
16.17. Componentes
Os componentes podem ser usados de quase todas as formas que os tipos de valores simples
são usados nas consultas HQL. Eles podem aparecer na cláusula select:
293
Capítulo 16. HQL: A Linguagem...
Esta é uma sintáxe válida, embora um pouco verbosa. Seria ótimo tornar essa sintáxe um pouco
mais concisa e utilizar a sintáxe row value constructor:
Com o uso da sintáxe row value constructor, e que pode ser de benéfico, seria quando utilizar
as subconsultas que precisem comparar com os valores múltiplos:
Ao decidir se você quer usar esta sintáxe ou não, deve-se considerar o fato de que a consulta
será dependente da ordenação das sub-propriedades do componente no metadados.
294
Consultas por critérios
O Hibernate provê uma API de consulta por critério intuitiva e extensível.
295
Capítulo 17. Consultas por cr...
Uma maneira alternativa de obter um critério é apartir de uma instância Property. Você pode
criar uma Property chamando Property.forName():
296
Associações
17.4. Associações
Através da navegação de associações usando createCriteria(), você pode especificar
restrições por entidades relacionadas:
Note que o segundo createCriteria() retorna uma nova instância de Criteria, que refere aos
elementos da coleção kittens.
Note que as coleções de kittens mantidas pelas instâncias Cat, retornadas pelas duas consultas
anteriores não são pré-filtradas pelo critério. Se você desejar recuperar somente os kittens que
se encaixarem ao critérios, você deverá usar um ResultTransformer.
Você pode ainda manipular o conjunto do resultado usando a junção exterior restante:
297
Capítulo 17. Consultas por cr...
.list();
Isto retornará todos os Cats com um mate (amigo) cujo nome inicia com "bom" ordenado pela
idade de seu mate e todos os cats que não tem mates. Isto é útil quando houver necessidade
de pedir ou limitar a prioridade do banco de dados em retornar conjuntos de resultado complexo/
grande e remover muitas instâncias onde consultas múltiplas deveriam ter sido executadas e os
resultados unidos pelo java em memória.
Sem este recurso, o primeiro de todos os cats sem um mate teria que ser carregado em uma
consulta.
Uma segunda consulta teria que restaurar os cats com os mates cujos os nomes iniciem com
"bom" selecionados pelas idades dos mates.
This query will fetch both mate and kittens by outer join. See Seção 21.1, “Estratégias de Busca
” for more information.
298
Projeções, agregações e agrupamento.
Você pode até usar os exemplos para colocar os critérios em objetos associados.
Não há necessidade de um "agrupamento por" explícito em uma consulta por critério. Certos
tipos de projeção são definidos para serem projeções de agrupamento, que também aparecem
em uma cláusula agrupamento porSQL.
Um alias pode ser atribuído de forma opcional à uma projeção, assim o valor projetado pode ser
referenciado em restrições ou ordenações. Aqui seguem duas formas diferentes para fazer isto:
299
Capítulo 17. Consultas por cr...
Os métodos alias() e as() simplesmente envolvem uma instância de projeção à outra instância
de Projeção em alias. Como um atalho, você poderá atribuir um alias quando adicionar a projeção
à uma lista de projeção:
300
Consultas e subconsultas desanexadas.
.add( Property.forName("weight").avg().as("avgWeight") )
.add( Property.forName("weight").max().as("maxWeight") )
.add( Property.forName("color").group().as("color" )
)
.addOrder( Order.desc("catCountByColor") )
.addOrder( Order.desc("avgWeight") )
.list();
Um DetachedCriteria também pode ser usado para expressar uma subconsulta. As instâncias
de critérios, que envolvem subconsultas, podem ser obtidas através das Subqueries ou
Property.
301
Capítulo 17. Consultas por cr...
Primeiro, você deve mapear a chave natural de sua entidade usando um <natural-id> e habilitar
o uso de um cache de segundo nível.
<class name="User">
<cache usage="read-write"/>
<id name="id">
<generator class="increment"/>
</id>
<natural-id>
<property name="name"/>
<property name="org"/>
</natural-id>
<property name="password"/>
</class
>
Note que esta funcionalidade não é proposta para o uso com entidades com chaves naturais
mutáveis.
Uma vez que você tenha ativado o cache de consulta Hibernate, o Restrictions.naturalId()
nos permite que utilizemos um algoritmo de cache mais eficiente.
session.createCriteria(User.class)
.add( Restrictions.naturalId()
.set("name", "gavin")
.set("org", "hb")
).setCacheable(true)
.uniqueResult();
302
SQL Nativo
Você também pode expressar consultas no dialeto SQL nativo de seu banco de dados. Isto é
bastante útil para usar recursos específicos do banco de dados, assim como dicas de consultas
ou a palavra chave em Oracle CONNECT. Ele também oferece um caminho de migração limpo de
uma aplicação baseada em SQL/JDBC direta até o Hibernate.
O Hibernate3 permite que você especifique o SQL escrito à mão, incluindo procedimentos
armazenados, para todas as operações de criar, atualizar, deletar e carregar.
Eles irão retornar uma matriz de Lista de Objeto (Object[]) com valores escalares para cada coluna
na tabela CATS. O Hibernate usará o ResultSetMetadata para deduzir a ordem atual e tipos de
valores escalares retornados.
Para evitar o uso do ResultSetMetadata ou simplesmente para ser mais explícito em o quê é
retornado, você poderá usar o addScalar():
Este ainda irá retornar as matrizes de Objeto, mas desta vez ele não usará o ResultSetMetdata,
ao invés disso, obterá explicitamente a coluna de ID, NOME e DATA DE NASCIMENTO como
303
Capítulo 18. SQL Nativo
É possível deixar de fora o tipo de informação para todos ou alguns dos escalares.
Esta é a mesma consulta de antes, mas desta vez, o ResultSetMetaData é utilizado para decidir
o tipo de NOME e DATA DE NASCIMENTO onde o tipo de ID é explicitamente especificado.
Considerando que o Cat esteja mapeado como uma classe com colunas ID,NOME e DATA DE
NASCIMENTO, as consultas acima irão devolver uma Lista onde cada elemento é uma entidade
de Cat.
Se a entidade estiver mapeada com um muitos-para-um para outra entidade, requer-se que
devolva também este ao desempenhar a consulta nativa, senão ocorrerá um erro de banco
de dados específico "coluna não encontrada". As colunas adicionais serão automaticamente
retornadas ao usar a anotação, mas preferimos ser explícitos como no seguinte exemplo para
umamuitos-para-um para um Dog:
304
Manuseio de associações e coleções
sess.createSQLQuery("SELECT c.ID, NAME, BIRTHDATE, DOG_ID, D_ID, D_NAME FROM CATS c, DOGS d
WHERE c.DOG_ID = d.D_ID")
.addEntity("cat", Cat.class)
.addJoin("cat.dog");
Neste exemplo, a devolução do Cat terá sua propriedade dog totalmente inicializada sem
nenhuma viagem extra ao banco de dados. Note que adicionamos um nome alias ("cat") para
poder especificar o caminho da propriedade alvo na união. É possível fazer a mesma união para
coleções, ex.: se ao invés disso, o Cat tivesse um-para-muitos para Dog.
sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, D_ID, D_NAME, CAT_ID FROM CATS c, DOGS d WHERE
c.ID = d.CAT_ID")
.addEntity("cat", Cat.class)
.addJoin("cat.dogs");
Neste estágio, estamos chegando no limite do que é possível fazer com as consultas nativas sem
começar a destacar as colunas sql para torná-las útil no Hibernate. Os problemas começam a
surgir quando se retorna entidades múltiplas do mesmo tipo ou quando o padrão de nomes de
alias/coluna não são suficientes.
É necessário uma injeção de alias de coluna na seguinte consulta (a qual é bem provável que
falhe):
A intenção para esta consulta é retornar duas instâncias Cat por linha: um cat e sua mãe. Isto
irá falhar pois existe um conflito de nomes, são mapeados aos mesmos nomes de colunas e em
alguns bancos de dados os aliases de colunas retornadas estarão, muito provavelmente, na forma
305
Capítulo 18. SQL Nativo
de "c.ID", "c.NOME", etc., os quais não são iguais às colunas especificadas no mapeamento ("ID"
e "NOME").
• a string da consulta SQL, com espaço reservado para Hibernate para injetar aliases de coluna.
A anotação {cat.*} e {mãe.*} usada acima, é um atalho para "todas as propriedades". De forma
alternativa, você pode listar as colunas explicitamente, mas até neste caso nós deixamos o
Hibernate injetar os aliases de coluna SQL para cada propriedade. O espaço reservado para
um alias de coluna é simplesmente o nome de propriedade qualificado pelo alias de tabela. No
seguinte exemplo, recuperamos os Cats e suas mães de uma tabela diferente (cat_log) para
aquele declarado no metadado de mapeamentos. Note que podemos até usar os aliases de
propriedade na cláusula where se quisermos.
As seguintes tabelas mostram as diferentes formas de usar uma injeção de alias. Por favor
note que os nomes de alias no resultado são exemplos, cada alias terá um nome único e
provavelmente diferente quando usado.
306
Retorno de entidades não gerenciadas
Discriminador de {[aliasname].class}
DISC as {item.class}
uma entidade
Todas as {[aliasname].*} {item.*}
propriedades de
uma entidade
Uma chave de {[aliasname].key} ORGID as {coll.key}
coleção
O id de uma {[aliasname].id} EMPID as {coll.id}
coleção
O elemento de uma {[aliasname].element}
XID as {coll.element}
coleção
propriedade de {[aliasname].element.
NAME as {coll.element.name}
elemento na [propertyname]}
coleção
Todas as {[aliasname].element.*}
{coll.element.*}
propriedades de
elemento na
coleção
Todas as {[aliasname].*} {coll.*}
propriedades da
coleção
• um transformador de resultado
A consulta acima irá devolver uma lista de CatDTO que foi instanciada e injetada com valores dos
comandos NAME e BIRTHDATE em suas propriedades correspondentes ou campos.
307
Capítulo 18. SQL Nativo
18.1.7. Parâmetros
Consultas sql Nativas suportam parâmetros posicionais assim como parâmetros nomeados:
Exemplo 18.1. Named sql query using the <sql-query> maping element
<sql-query name="persons">
<return alias="person" class="eg.Person"/>
SELECT person.NAME AS {person.name},
person.AGE AS {person.age},
person.SEX AS {person.sex}
FROM PERSON person
WHERE person.NAME LIKE :namePattern
</sql-query>
<sql-query name="personsWith">
308
Consultas SQL Nomeadas
Uma consulta SQL nomeada pode devolver um valor escalar. Você deve declarar um alias de
coluna e um tipo Hibernate usando o elemento <return-scalar>:
<sql-query name="mySqlQuery">
<return-scalar column="name" type="string"/>
<return-scalar column="age" type="long"/>
SELECT p.NAME AS name,
p.AGE AS age,
FROM PERSON p WHERE p.NAME LIKE 'Hiber%'
</sql-query>
<resultset name="personAddress">
<return alias="person" class="eg.Person"/>
<return-join alias="address" property="person.mailingAddress"/>
</resultset>
309
Capítulo 18. SQL Nativo
Você pode também, como forma alternativa, usar a informação de mapeamento de conjunto de
resultado em seus arquivos hbm em código de java.
So far we have only looked at externalizing SQL queries using Hibernate mapping
files. The same concept is also available with anntations and is called named native
queries. You can use @NamedNativeQuery (@NamedNativeQueries) in conjunction with
@SqlResultSetMapping (@SqlResultSetMappings). Like @NamedQuery, @NamedNativeQuery
and @SqlResultSetMapping can be defined at class level, but their scope is global to the
application. Lets look at a view examples.
In Exemplo 18.8, “Implicit result set mapping” the result set mapping is implicit. We only describe
the entity class of the result set mapping. The property / column mappings is done using the entity
mapping values. In this case the model property is bound to the model_txt column.
Finally, if the association to a related entity involve a composite primary key, a @FieldResult
element should be used for each foreign key column. The @FieldResult name is composed of
the property name for the relationship, followed by a dot ("."), followed by the name or the field or
property of the primary key. This can be seen in Exemplo 18.9, “Using dot notation in @FieldResult
for specifying associations ”.
310
Consultas SQL Nomeadas
@Entity
@SqlResultSetMapping(name="implicit",
entities=@EntityResult(entityClass=SpaceShip.class))
@NamedNativeQuery(name="implicitSample",
query="select * from SpaceShip",
resultSetMapping="implicit")
public class SpaceShip {
private String name;
private String model;
private double speed;
@Id
public String getName() {
return name;
}
@Column(name="model_txt")
public String getModel() {
return model;
}
311
Capítulo 18. SQL Nativo
@Entity
@SqlResultSetMapping(name="compositekey",
entities=@EntityResult(entityClass=SpaceShip.class,
fields = {
@FieldResult(name="name", column = "name"),
@FieldResult(name="model", column = "model"),
@FieldResult(name="speed", column = "speed"),
@FieldResult(name="captain.firstname", column = "firstn"),
@FieldResult(name="captain.lastname", column = "lastn"),
@FieldResult(name="dimensions.length", column = "length"),
@FieldResult(name="dimensions.width", column = "width")
}),
columns = { @ColumnResult(name = "surface"),
@ColumnResult(name = "volume") } )
@NamedNativeQuery(name="compositekey",
query="select name, model, speed, lname as lastn, fname as firstn, length, width, length
* width as surface from SpaceShip",
resultSetMapping="compositekey")
} )
public class SpaceShip {
private String name;
private String model;
private double speed;
private Captain captain;
private Dimensions dimensions;
@Id
public String getName() {
return name;
}
@ManyToOne(fetch= FetchType.LAZY)
@JoinColumns( {
@JoinColumn(name="fname", referencedColumnName = "firstname"),
@JoinColumn(name="lname", referencedColumnName = "lastname")
} )
public Captain getCaptain() {
return captain;
}
312
Consultas SQL Nomeadas
@Entity
@IdClass(Identity.class)
public class Captain implements Serializable {
private String firstname;
private String lastname;
@Id
public String getFirstname() {
return firstname;
}
@Id
public String getLastname() {
return lastname;
}
Dica
If you retrieve a single entity using the default mapping, you can specify the
resultClass attribute instead of resultSetMapping:
313
Capítulo 18. SQL Nativo
In some of your native queries, you'll have to return scalar values, for example when building
report queries. You can map them in the @SqlResultsetMapping through @ColumnResult. You
actually can even mix, entities and scalar returns in the same native query (this is probably not
that common though).
@SqlResultSetMapping(name="scalar", columns=@ColumnResult(name="dimension"))
@NamedNativeQuery(name="scalar", query="select length*width as dimension from
SpaceShip", resultSetMapping="scalar")
An other query hint specific to native queries has been introduced: org.hibernate.callable
which can be true or false depending on whether the query is a stored procedure or not.
Com a <return-property> você pode informar explicitamente, quais aliases de coluna utilizar,
ao invés de usar a sintáxe {} para deixar o Hibernate injetar seus próprios aliases. Por exemplo:
<sql-query name="mySqlQuery">
<return alias="person" class="eg.Person">
<return-property name="name" column="myName"/>
<return-property name="age" column="myAge"/>
<return-property name="sex" column="mySex"/>
</return>
SELECT person.NAME AS myName,
person.AGE AS myAge,
person.SEX AS mySex,
FROM PERSON person WHERE person.NAME LIKE :name
</sql-query>
<return-property> também funciona com colunas múltiplas. Isto resolve a limitação com a
sintáxe {} que não pode permitir controle granulado fino de muitas propriedades de colunas
múltiplas.
<sql-query name="organizationCurrentEmployments">
<return alias="emp" class="Employment">
<return-property name="salary">
314
Usando procedimentos de armazenamento para consultas
<return-column name="VALUE"/>
<return-column name="CURRENCY"/>
</return-property>
<return-property name="endDate" column="myEndDate"/>
</return>
SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
REGIONCODE as {emp.regionCode}, EID AS {emp.id}, VALUE, CURRENCY
FROM EMPLOYMENT
WHERE EMPLOYER = :id AND ENDDATE IS NULL
ORDER BY STARTDATE ASC
</sql-query>
Observe que neste exemplo nós usamos <return-property> combinado à síntáxe {} para
injeção. Permite que os usuários escolham como eles querem se referir à coluna e às
propriedades.
Para usar esta consulta no Hibernate você vai precisar mapeá-lo através de uma consulta
nomeada
315
Capítulo 18. SQL Nativo
O formulário de chamada
recomedado é o padrão SQL92: { ? = call
functionName(<parameters>) } or { ? = call procedureName(<parameters>}. A sintáxe
de chamada nativa não é suportada.
• O procedimento deve retornar um conjunto de resultados. Observe que, como este servidor
pode retornar múltiplos conjuntos de resultados e contas atualizadas, o Hibernate irá inteirar os
resultados e pegar o primeiro resultado, o qual é o valor de retorno do conjunto de resultados.
O resto será descartado.
• Se você habilitar SET NOCOUNT ON no seu procedimento, ele provavelmente será mais eficiente.
Mas, isto não é obrigatório
316
SQL padronizado para criar, atualizar e deletar
@Entity
@Table(name="CHAOS")
@SQLInsert( sql="INSERT INTO CHAOS(size, name, nickname, id) VALUES(?,upper(?),?,?)")
@SQLUpdate( sql="UPDATE CHAOS SET size = ?, name = upper(?), nickname = ? WHERE id = ?")
@SQLDelete( sql="DELETE CHAOS WHERE id = ?")
@SQLDeleteAll( sql="DELETE CHAOS")
@Loader(namedQuery = "chaos")
@NamedNativeQuery(name="chaos", query="select id, size, name, lower( nickname ) as nickname from
CHAOS where id= ?", resultClass = Chaos.class)
public class Chaos {
@Id
private Long id;
private Long size;
private String name;
private String nickname;
<class name="Person">
<id name="id">
<generator class="increment"/>
</id>
<property name="name" not-null="true"/>
<sql-insert>INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? )</sql-insert>
<sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?</sql-update>
<sql-delete>DELETE FROM PERSON WHERE ID=?</sql-delete>
</class>
If you expect to call a store procedure, be sure to set the callable attribute to true. In annotations
as well as in xml.
To check that the execution happens correctly, Hibernate allows you to define one of those three
strategies:
317
Capítulo 18. SQL Nativo
• none: no check is performed: the store procedure is expected to fail upon issues
• param: like COUNT but using an output parameter rather that the standard mechanism
To define the result check style, use the check parameter which is again available in annoations
as well as in xml.
You can use the exact same set of annotations respectively xml nodes to override the collection
related statements -see Exemplo 18.13, “Overriding SQL statements for collections using
annotations”.
@OneToMany
@JoinColumn(name="chaos_fk")
@SQLInsert( sql="UPDATE CASIMIR_PARTICULE SET chaos_fk = ? where id = ?")
@SQLDelete( sql="UPDATE CASIMIR_PARTICULE SET chaos_fk = null where id = ?")
private Set<CasimirParticle> particles = new HashSet<CasimirParticle>();
Dica
The parameter order is important and is defined by the order Hibernate handles
properties. You can see the expected order by enabling debug logging for the
org.hibernate.persister.entity level. With this level enabled Hibernate will
print out the static SQL that is used to create, update, delete etc. entities. (To
see the expected sequence, remember to not include your custom SQL through
annotations or mapping files as that will override the Hibernate generated static sql)
@Entity
@SecondaryTables({
@SecondaryTable(name = "`Cat nbr1`"),
@SecondaryTable(name = "Cat2"})
@org.hibernate.annotations.Tables( {
@Table(appliesTo = "Cat", comment = "My cat table" ),
@Table(appliesTo = "Cat2", foreignKey = @ForeignKey(name="FK_CAT2_CAT"), fetch = FetchMode.SELECT,
sqlInsert=@SQLInsert(sql="insert into Cat2(storyPart2, id) values(upper(?), ?)") )
} )
318
SQL padronizado para carga
The previous example also shows that you can give a comment to a given table (primary or
secondary): This comment will be used for DDL generation.
Dica
The SQL is directly executed in your database, so you can use any dialect you
like. This will, however, reduce the portability of your mapping if you use database
specific SQL.
Last but not least, stored procedures are in most cases required to return the number of rows
inserted, updated and deleted. Hibernate always registers the first statement parameter as a
numeric output parameter for the CUD operations:
update PERSON
set
NAME = uname,
where
ID = uid;
return SQL%ROWCOUNT;
END updatePerson;
<sql-query name="person">
<return alias="pers" class="Person" lock-mode="upgrade"/>
SELECT NAME AS {pers.name}, ID AS {pers.id}
FROM PERSON
WHERE ID=?
FOR UPDATE
</sql-query>
319
Capítulo 18. SQL Nativo
Este é apenas uma instrução de consulta nomeada, como discutido anteriormente. Você pode
referenciar esta consulta nomeada em um mapeamento de classe:
<class name="Person">
<id name="id">
<generator class="increment"/>
</id>
<property name="name" not-null="true"/>
<loader query-ref="person"/>
</class>
Você pode também definir uma consulta para carregar uma coleção:
<sql-query name="employments">
<load-collection alias="emp" role="Person.employments"/>
SELECT {emp.*}
FROM EMPLOYMENT emp
WHERE EMPLOYER = :id
ORDER BY STARTDATE ASC, EMPLOYEE ASC
</sql-query>
Você pode até definir um carregador de entidade que carregue uma coleção por busca de união:
<sql-query name="person">
<return alias="pers" class="Person"/>
<return-join alias="emp" property="pers.employments"/>
SELECT NAME AS {pers.*}, {emp.*}
FROM PERSON pers
LEFT OUTER JOIN EMPLOYMENT emp
ON pers.ID = emp.PERSON_ID
WHERE ID=?
</sql-query>
The annotation equivalent <loader> is the @Loader annotation as seen in Exemplo 18.11,
“Custom CRUD via annotations”.
320
Filtrando dados
O Hibernate3 provê um novo método inovador para manusear dados com regras de "visibilidade".
Um Filtro do Hibernate é um filtro global, nomeado e parametrizado que pode ser habilitado ou
não dentro de uma Sessão do Hibernate.
We now need to define the SQL filter clause applied to either the entity load or the collection load.
@Filter is used and placed either on the entity or the collection element. The connection between
@FilterName and @Filter is a matching name.
@Entity
@FilterDef(name="minLength", parameters=@ParamDef( name="minLength", type="integer" ) )
@Filters( {
@Filter(name="betweenLength", condition=":minLength <= length and :maxLength >= length"),
@Filter(name="minLength", condition=":minLength <= length")
} )
public class Forest { ... }
When the collection use an association table as a relational representation, you might want to
apply the filter condition to the association table itself or to the target entity table. To apply the
constraint on the target entity, use the regular @Filter annotation. However, if you want to target
the association table, use the @FilterJoinTable annotation.
321
Capítulo 19. Filtrando dados
@OneToMany
@JoinTable
//filter on the target entity table
@Filter(name="betweenLength", condition=":minLength <= length and :maxLength >= length")
//filter on the association table
@FilterJoinTable(name="security", condition=":userlevel >= requredLevel")
public Set<Forest> getForests() { ... }
Using Hibernate mapping files for defining filters the situtation is very similar. The filters must first
be defined and then attached to the appropriate mapping elements. To define a filter, use the
<filter-def/> element within a <hibernate-mapping/> element:
<filter-def name="myFilter">
<filter-param name="myFilterParam" type="string"/>
</filter-def>
This filter can then be attached to a class or collection (or, to both or multiples of each at the
same time):
<set ...>
<filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
</set>
</class>
session.enableFilter("myFilter").setParameter("myFilterParam", "some-value");
322
Filtros do Hibernate
Um exemplo completo, usando dados temporais com um padrão de datas de registro efetivo:
<filter-def name="effectiveDate">
<filter-param name="asOfDate" type="date"/>
</filter-def>
Para garantir que você sempre tenha registro efetivos, simplesmente habilite o filtro na sessão
antes de recuperar os dados dos empregados:
No HQL acima, mesmo que tenhamos mencionado apenas uma restrição de salário nos
resultados, por causa do filtro habilitado, a consulta retornará apenas os funcionários ativos cujo
salário é maior que um milhão de dólares.
Nota: se você planeja usar filtros com união externa (por HQL ou por busca de carga) seja
cuidadoso quanto à direção da expressão de condição. É mais seguro configurá-lo para uma
união externa esquerda. Coloque o parâmetro primeiro seguido pelo(s) nome(s) da coluna após
o operador.
Após ser definido, o filtro deve ser anexado às entidades múltiplas e/ou coleções, cada uma com
sua própria condição. Isto pode ser tedioso quando as condições se repetem. Assim, usando o
<filter-def/> permite denifir uma condição padrão, tanto como uma função quanto CDATA:
323
Capítulo 19. Filtrando dados
Esta condição padrão será utilizada todas as vezes que um filtro for anexado a algo sem uma
condição específica. Note que isto significa que você pode dar uma condição específica como
parte de um anexo de filtro que substitua a condição padrão neste caso em particular.
324
Mapeamento XML
XML Mapping is an experimental feature in Hibernate 3.0 and is currently under active
development.
O Hibernate suporta a API dom4j para manipular árvores XML. Você pode escrever queries que
retornem árvores dom4j do banco de dados e automaticamente sincronizar com o banco de
dados qualquer modificação feita nessas árvores. Você pode até mesmo pegar um documento
XML, analisá-lo usando o dom4j, e escrever as alterações no banco de dados usando
quaisquer operações básicas do Hibernate: persist(), saveOrUpdate(),merge(), delete(),
replicate() (a mesclagem ainda não é suportada)
Um mapeamento simples pode ser usado para simultaneamente mapear propriedades da classe
e nós de um documento XML para um banco de dados ou, se não houver classe para mapear,
pode ser usado simplesmente para mapear o XML.
<class name="Account"
table="ACCOUNTS"
node="account">
<id name="accountId"
column="ACCOUNT_ID"
node="@id"/>
<many-to-one name="customer"
column="CUSTOMER_ID"
node="customer/@id"
embed-xml="false"/>
<property name="balance"
column="BALANCE"
node="balance"/>
325
Capítulo 20. Mapeamento XML
...
</class
>
<class entity-name="Account"
table="ACCOUNTS"
node="account">
<id name="id"
column="ACCOUNT_ID"
node="@id"
type="string"/>
<many-to-one name="customerId"
column="CUSTOMER_ID"
node="customer/@id"
embed-xml="false"
entity-name="Customer"/>
<property name="balance"
column="BALANCE"
node="balance"
type="big_decimal"/>
...
</class
>
Esse mapeamento permite que você acesse os dados como uma árvore dom4j ou um gráfico de
pares de nome/valor de propriedade ou Maps do Java. Os nomes de propriedades são somente
construções lógicas que podem ser referenciadas em consultas HQL.
326
Mapeando metadados com XML
Para coleções e associações de valores simples, existe uma função adicional embed-xml. Se a
função embed-xml="true", que é o valor padrão, a árvore XML para a entidade associada (ou
coleção de determinado tipo de valor) será embutida diretamente na árvore XML que contém
a associação. Por outro lado, se embed-xml="false", então apenas o valor do identificador
referenciado irá aparecer no XML para associações simples e as coleções simplesmente não
irão aparecer.
Você precisa tomar cuidado para não deixar o embed-xml="true" para muitas associações, pois
o XML não suporta bem referências circulares.
<class name="Customer"
table="CUSTOMER"
node="customer">
<id name="id"
column="CUST_ID"
node="@id"/>
<map name="accounts"
node="."
embed-xml="true">
<key column="CUSTOMER_ID"
not-null="true"/>
<map-key column="SHORT_DESC"
node="@short-desc"
type="string"/>
<one-to-many entity-name="Account"
embed-xml="false"
node="account"/>
</map>
<component name="name"
node="name">
<property name="firstName"
node="first-name"/>
<property name="initial"
node="initial"/>
<property name="lastName"
node="last-name"/>
</component>
...
</class
>
Nesse caso, decidimos incorporar a coleção de ids de contas, e não os dados de contas. Segue
a abaixo a consulta HQL:
from Customer c left join fetch c.accounts where c.lastName like :lastName
327
Capítulo 20. Mapeamento XML
<customer id="123456789">
<account short-desc="Savings"
>987632567</account>
<account short-desc="Credit Card"
>985612323</account>
<name>
<first-name
>Gavin</first-name>
<initial
>A</initial>
<last-name
>King</last-name>
</name>
...
</customer
>
<customer id="123456789">
<account id="987632567" short-desc="Savings">
<customer id="123456789"/>
<balance
>100.29</balance>
</account>
<account id="985612323" short-desc="Credit Card">
<customer id="123456789"/>
<balance
>-2370.34</balance>
</account>
<name>
<first-name
>Gavin</first-name>
<initial
>A</initial>
<last-name
>King</last-name>
</name>
...
</customer
>
328
Manipulando dados em XML
tx.commit();
session.close();
tx.commit();
session.close();
329
330
Aumentando o desempenho
21.1. Estratégias de Busca
Uma estratégia de busca é a estratégia que o Hibernate irá usar para recuperar objetos
associados se a aplicação precisar navegar pela associação. Estratégias de Busca podem ser
declaradas nos metadados de mapeamento O/R, ou sobrescritos por uma consulta HQL ou
consulta com Criteria.
• Join fetching - o Hibernate busca o objeto ou coleção associada no mesmo SELECT, usando
um OUTER JOIN.
• Select fetching - um segundo SELECT é usado para buscar a entidade ou coleção associada. A
menos que você desabilite a busca lazy, especificando lazy="false", esse segundo SELECT
será executado apenas quando você acessar a associação.
• Subselect fetching - um segundo SELECT será usado para recuperar as coleções associadas
de todas as entidades recuperadas em uma consulta ou busca anterior. A menos que você
desabilite a busca lazy especificando lazy="false", esse segundo SELECT será executado
apenas quando você acessar a associação.
• Batch fetching - uma opção de otimização para selecionar a busca. O Hibernate recupera um
lote de instâncias ou entidades usando um único SELECT, especificando uma lista de chaves
primárias ou chaves externas.
• Lazy collection fetching - a coleção é recuperada quando a aplicação invoca uma operação
sobre aquela coleção. Esse é o padrão para coleções.
• "Extra-lazy" collection fetching - elementos individuais de uma coleção são acessados a partir
do banco de dados quando necessário. O Hibernate tenta não buscar a coleção inteira dentro
da memória a menos que seja absolutamente necessário. Isto é indicado para coleções muito
grandes.
• Proxy fetching: uma associação de um valor é carregada quando um método diferente do getter
do identificador é invocado sobre o objeto associado.
331
Capítulo 21. Aumentando o des...
(lazy); a associação é buscada até mesmo quando somente o identificador é acessado. Ela
é mais transparente, já que não há proxies visíveis para a aplicação. Esse método requer
instrumentação de bytecodes em build-time e é raramente necessário.
Nós temos aqui duas noções ortogonais: quando a associação é buscada e como ela é buscada.
É importante que você não os confuda. Nós usamos fetch para ajustar o desempenho. Podemos
usar lazy para definir um contrato para qual dado é sempre disponível em qualquer instância
desconectada de uma classe particular.
Perceba que o acesso a associações preguiçosas fora do contexto de uma sessão aberta do
Hibernate irá resultar numa exceção. Por exemplo:
s = sessions.openSession();
Transaction tx = s.beginTransaction();
tx.commit();
s.close();
Como a coleção de permissões não foi inicializada quando a Session for fechada, a coleção
não poderá carregar o seu estado. O Hibernate não suporta inicialização preguiçosa para objetos
desconectados. Para consertar isso, é necessário mover o código que carrega a coleção para
logo antes da transação ser submetida.
Alternativamente, nós podemos usar uma coleção ou associação não preguiçosa, especificando
lazy="false" para o mapeamento da associação. Porém, é pretendido que a inicialização
preguiçosa seja usada por quase todas as coleções e associações. Se você definir muitas
associações não preguiçosas em seu modelo de objetos, o Hibernate irá precisar buscar no banco
de dados inteiro da memória em cada transação.
332
Personalizando as estratégias de busca
Por outro lado, nós geralmente escolhemos a busca de união (que não é preguiçosa por natureza)
ao invés do selecionar busca em uma transação particular. Nós agora veremos como customizar
a estratégia de busca. No Hibernate3, os mecanismos para escolher a estratégia de busca são
idênticos para as associações de valor único e para coleções.
<set name="permissions"
fetch="join">
<key column="userId"/>
<one-to-many class="Permission"/>
</set
Independentemente da estratégia de busca que você usar, o gráfico não preguiçoso definido será
certamente carregado na memória. Note que isso irá resultar em diversas seleções imediatas
sendo usadas para rodar uma consulta HQL em particular.
Se você quiser mudar a estratégia de busca usada pelo get() ou load(), simplesmente use
uma consulta por Criteria, por exemplo:
333
Capítulo 21. Aumentando o des...
Isto é o equivalente do Hibernate para o que algumas soluções ORM chamam de "plano de
busca".
Um meio totalmente diferente de evitar problemas com selects N+1 é usar um cache de segundo
nível.
Por padrão, o Hibernate3 gera proxies (na inicialização) para todas as classes persistentes que
os usem para habilitar recuperação preguiçosa de associações many-to-one e one-to-one.
O arquivo de mapeamento deve declarar uma interface para usar como interface de proxy para
aquela classe, com a função proxy. Por padrão, o Hibernate usa uma subclasse dessa classe.
Note que a classe a ser usada via proxy precisa implementar o construtor padrão com pelo menos
visibilidade de package. Nós recomendamos esse construtor para todas as classes persistentes.
Existe alguns truques que você deve saber quando estender esse comportamento para classes
polimórficas. Por exemplo:
Primeiramente, instâncias de Cat nunca serão convertidas para DomesticCat, mesmo que a
instância em questão seja uma instância de DomesticCat:
Cat cat = (Cat) session.load(Cat.class, id); // instantiate a proxy (does not hit the db)
if ( cat.isDomesticCat() ) { // hit the db to initialize the proxy
DomesticCat dc = (DomesticCat) cat; // Error!
....
}
334
Proxies de associação final único
System.out.println(cat==dc); // false
Porém a situação não é tão ruim como parece. Mesmo quando temos duas referências para
objetos proxies diferentes, a instância adjacente será do mesmo objeto:
E por terceiro, você não pode usar um proxy CGLIB em uma classe final ou com quaisquer
métodos final.
Finalmente, se o seu objeto persistente adquirir qualquer recurso durante a instanciação (ex. em
inicializadores ou construtor padrão), então esses recursos serão adquiridos pelo proxy também.
A classe de proxy é uma subclasse da classe persistente.
Esses problemas se dão devido à limitação originária do modelo de herança simples do Java.
Se você quiser evitar esses problemas em suas classes persistentes você deve implementar
uma interface que declare seus métodos comerciais. Você deve especificar essas interfaces
no arquivo de mapeamento onde CatImpl implementa a interface Cat e DomesticCatImpl
implementa a interface DomesticCat. Por exemplo:
Então, os proxies para instâncias de Cat e DomesticCat podem ser retornadas pelo load() ou
iterate().
Nota
Relacionamentos são também inicializados de forma preguiçosa. Isso significa que você precisa
declarar qualquer propriedade como sendo do tipo Cat, e não CatImpl.
335
Capítulo 21. Aumentando o des...
Algumas vezes precisamos garantir que o proxy ou coleção é inicializado antes de fechar
a Session. Claro que sempre podemos forçar a inicialização chamando cat.getSex() ou
cat.getKittens().size(), por exemplo. Mas isto parece confuso para quem lê o código e não
é conveniente para códigos genéricos.
Uma outra opção é manter a Session aberta até que todas as coleções e os proxies necessários
sejam carregados. Em algumas arquiteturas de aplicações, particularmente onde o código que
acessa os dados usando Hibernate e o código que os usa, se encontram em diferentes camadas
da aplicação ou diferentes processos físicos, será um problema garantir que a Session esteja
aberta quando uma coleção for inicializada. Existem dois caminhos básicos para lidar com esse
problema:
• Em uma aplicações web, um filtro servlet pode ser usado para fechar a Session somente
no final da requisição do usuário, quando a renderização da view estiver completa (o modelo
Abrir Sessão em View). Claro, que isto demanda uma exatidão no manuseio de exceções na
infraestrutura de sua aplicação. É extremamente importante que a Session seja fechada e a
transação terminada antes de retornar para o usuário, mesmo que uma exceção ocorra durante
a renderização da view. Veja o Wiki do Hibernate para exemplos do pattern "Abrir Sessão em
View".
• Em uma aplicação com uma camada de negócios separada, a lógica de negócios deve
"preparar" todas as coleções que serão usadas pela camada web antes de retornar. Isto
sgnifica que a camada de negócios deve carregar todos os dados e retorná-los já inicializados
para a camada de apresentação que é representada para um caso de uso particular.
Geralmente, a aplicação chama Hibernate.initialize() para cada coleção que será usada
pela camada web (essa chamada deve ocorrer antes da sessão ser fechada) ou retorna a
336
Usando busca em lote
coleção usando uma consulta Hibernate com uma cláusula FETCH ou um FetchMode.JOIN
na Criteria. Fica muito mais fácil se você adotar o modelo Command ao invés do Session
Facade.
• Você também pode anexar um objeto previamente carregado em uma nova Sessionmerge()
ou lock() antes de acessar coleções não inicializadas (ou outros proxies). O Hibernate não
faz e certamente não deve fazer isso automaticamente, pois isso introduziria semântica em
transações impromptu.
Às vezes você não quer inicializar uma coleção muito grande, mas precisa de algumas
informações, como o mesmo tamanho, ou um subconjunto de seus dados.
Você pode usar um filtro de coleção para saber seu tamanho sem inicializá-la:
O método createFilter() é usado também para retornar algus dados de uma coleção
eficientemente sem precisar inicializar a coleção inteira:
A recuperação em lote para classes/entidades é mais fácil de entender. Imagine que você tem
a seguinte situação em tempo de execução: você tem 25 instâncias de Cat carregadas em uma
Session, cada Cat possui uma referência ao seu owner, que é da classe Person. A classe
Person é mapeada com um proxy, lazy="true". Se você interar sobre todos os Cat's e chamar
getOwner() em cada, o Hibernate irá por padrão executar 25 comandos SELECT(), para buscar
os proxies de owners. Você pode melhorar esse comportamento especificando um batch-size
no mapeamento da classe Person:
O Hibernate irá executar agora apenas três consultas; o padrão é 10, 10, 5.
Você também pode habilitar busca em lote de uma coleção. Por exemplo, se cada Person tem
uma coleção preguiçosa de Cats e 10 persons estão já carregadas em uma Session, serão
gerados 10 SELECTs ao se interar todas as persons, um para cada chamada de getCats().
337
Capítulo 21. Aumentando o des...
Se você habilitar busca em lote para a coleção de cats no mapeamento da classe Person, o
Hibernate pode fazer uma pré carga das coleções:
<class name="Person">
<set name="cats" batch-size="3">
...
</set>
</class>
A busca em lote de coleções é particularmente útil quando você tem uma árvore encadeada
de ítens, ex.: o típico padrão bill-of-materials (Se bem que um conjunto encadeado ou caminho
materializado pode ser uma opção melhor para árvores com mais leitura.
So what does that mean? Well lets explain that by way of an example which show the different
available approaches to configure a fetch profile:
@Entity
@FetchProfile(name = "customer-with-orders", fetchOverrides = {
338
Perfis de Busca
@OneToMany
private Set<Order> orders;
// standard getter/setter
...
}
<hibernate-mapping>
<class name="Customer">
...
<set name="orders" inverse="true">
<key column="cust_id"/>
<one-to-many class="Order"/>
</set>
</class>
<class name="Order">
...
</class>
<fetch-profile name="customer-with-orders">
<fetch entity="Customer" association="orders" style="join"/>
</fetch-profile>
</hibernate-mapping>
<hibernate-mapping>
<class name="Customer">
...
<set name="orders" inverse="true">
<key column="cust_id"/>
<one-to-many class="Order"/>
</set>
<fetch-profile name="customer-with-orders">
<fetch association="orders" style="join"/>
</fetch-profile>
</class>
<class name="Order">
...
</class>
</hibernate-mapping>
Now normally when you get a reference to a particular customer, that customer's set of orders
will be lazy meaning we will not yet have loaded those orders from the database. Normally this
is a good thing. Now lets say that you have a certain use case where it is more efficient to load
339
Capítulo 21. Aumentando o des...
the customer and their orders together. One way certainly is to use "dynamic fetching" strategies
via an HQL or criteria queries. But another option is to use a fetch profile to achieve that. The
following code will load both the customer andtheir orders:
Nota
@FetchProfile definitions are global and it does not matter on which class you
place them. You can place the @FetchProfile annotation either onto a class or
package (package-info.java). In order to define multiple fetch profiles for the same
class or package @FetchProfiles can be used.
Para habilitar a carga de propriedade lazy, é preciso ajustar a função lazy no seu mapeamento
de propriedade:
<class name="Document">
<id name="id">
<generator class="native"/>
</id>
<property name="name" not-null="true" length="50"/>
<property name="summary" not-null="true" length="200" lazy="true"/>
<property name="text" not-null="true" length="2000" lazy="true"/>
</class>
340
O Cachê de Segundo Nível
<instrument verbose="true">
<fileset dir="${testclasses.dir}/org/hibernate/auction/model">
<include name="*.class"/>
</fileset>
</instrument>
</target>
Uma forma diferente de evitar leitura de coluna desnecessária, ao menos para transações de
somente leitura, deve-se usar os recursos de projeção do HQL ou consultas por Critério. Isto evita
a necessidade de processamento de bytecode em build-time e é certamente uma melhor solução.
Você pode forçar a busca antecipada comum de propriedades usando buscar todas as
propriedades no HQL.
You have the option to tell Hibernate which caching implementation to use by specifying the
name of a class that implements org.hibernate.cache.CacheProvider using the property
hibernate.cache.provider_class. Hibernate is bundled with a number of built-in integrations
with the open-source cache providers that are listed in Tabela 21.1, “Provedores de Cache ”. You
can also implement your own and plug it in as outlined above. Note that versions prior to Hibernate
3.2 use EhCache as the default cache provider.
341
Capítulo 21. Aumentando o des...
By default, entities are not part of the second level cache and we recommend you to stick to
this setting. However, you can override this by setting the shared-cache-mode element in your
persistence.xml file or by using the javax.persistence.sharedCache.mode property in your
configuration. The following values are possible:
• ENABLE_SELECTIVE (Default and recommended value): entities are not cached unless explicitly
marked as cacheable.
• ALL: all entities are always cached even if marked as non cacheable.
• NONE: no entity are cached even if marked as cacheable. This option can make sense to disable
second-level cache altogether.
342
Mapeamento de Cache
• read-only
• read-write
• nonstrict-read-write
• transactional
Nota
It is recommended to define the cache concurrency strategy per entity rather than
using a global one. Use the @org.hibernate.annotations.Cache annotation for
that.
@Entity
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Forest { ... }
Hibernate also let's you cache the content of a collection or the identifiers if the collection contains
other entities. Use the @Cache annotation on the collection property.
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
@JoinColumn(name="CUST_ID")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public SortedSet<Ticket> getTickets() {
return tickets;
}
@Cache(
343
Capítulo 21. Aumentando o des...
CacheConcurrencyStrategy usage();
Let's now take a look at Hibernate mapping files. There the <cache> element of a class or collection
mapping is used to configure the second level cache. Looking at Exemplo 21.8, “The Hibernate
<cache> mapping element” the parallels to anotations is obvious.
<cache
usage="transactional|read-write|nonstrict-read-write|read-only"
region="RegionName"
include="all|non-lazy"
/>
Se sua aplicação precisar ler mas nunca modificar instâncias de uma classe persistente, pode-
se utilizar um cache de read-only. Esta é a estratégia de desempenho mais simples e melhor.
É também perfeitamente seguro para uso em um cluster.
344
Estratégia: leitura/escrita
Importante
Nenhum provedor de cache suporta todas as estratégias de concorrência de
cache.
A seguinte tabela mostra qual provedor é compatível com qual estratégia de concorrência.
345
Capítulo 21. Aumentando o des...
Quando o flush() for subsequentemente chamado, o estado deste objeto será sincronizado
com o banco de dados. Se você não desejar que esta sincronização aconteça ou se você
estiver processando uma grande quantidade de objetos e precisar gerenciar a memória de forma
eficiente, o método evict() pode ser usado para remover o objeto de suas coleções de cache
de primeiro nível.
Exemplo 21.9. Explcitly evicting a cached instance from the first level cache
using Session.evict()
Para o cache de segundo nível, existem métodos definidos na SessionFactory para despejar o
estado de cache de uma instância, classe inteira, instância de coleção ou papel de coleção inteiro.
346
O Cache de Consulta
O CacheMode controla como uma sessão em particular interage com o cache de segundo nível:
• CacheMode.GET: itens de leitura do cache de segundo nível. Não escreve ao cache de segundo
nível, exceto quando atualizar dados.
• CacheMode.PUT: escreve itens ao cache de segundo nível. Não lê a partir do cache de segundo
nível.
• CacheMode.REFRESH: escreve itens ao cache de segundo nível, mas não lê a partir do cache
de segundo nível. Passa o efeito de hibernate.cache.use_minimal_puts, forçando uma
atualização do cache de segundo nível para que todos os itens leiam a partir do banco de
dados.
Para navegar o conteúdo do segundo nível ou região de cache de consulta, use oStatistics
API:
Exemplo 21.11. Browsing the second-level cache entries via the Statistics
API
hibernate.generate_statistics true
hibernate.cache.use_structured_entries true
347
Capítulo 21. Aumentando o des...
resultados de uma consulta do Person Hibernate, você precisará acompanhar quando estes
resultados deverão ser inválidos devido alterações salvas no Person. Tudo isto, acompanhado
com o fato de que a maioria dos aplicativos não recebem benefício algum ao realizar o cache
nos resultados da consulta, levando o Hibernate a desativar o cache de resultados de consulta
por padrão. Para uso do cache de consulta, você primeiro precisa ativar o cache de consulta:
hibernate.cache.use_query_cache true
Importante
Nota
348
Regiões de cache de consulta
Se você quiser forçar um cache de consulta para uma atualização de sua região
(independente de quaisquer resultados com cache encontrados nesta região), você poderá
usar org.hibernate.Query.setCacheMode(CacheMode.REFRESH). Juntamente com a região
que você definiu para o cache gerado, o Hibernate seletivamente forçará os resultados com
cache, naquela região particular a ser atualizada. Isto é particularmente útil em casos onde
dados adjacentes podem ter sido atualizados através de um processo em separado , além de
ser uma alternativa mais eficiente se aplicada ao despejo de uma região de cache através de
SessionFactory.evictQueries().
21.5.1. Taxonomia
O Hibernate define três tipos básicos de coleções:
• Coleções de valores
• Associações um-para-muitos
• Associações muitos-para-muitos
A classificação distingue as diversas tabelas e relacionamento de chave externa, mas não nos diz
tudo que precisamos saber sobre o modelo relacional. Para entender completamente a estrutura
relacional e as características de desempenho, devemos também considerar a estrutura da chave
primária que é usada pelo Hibernate para atualizar ou deletar linhas de coleções. Isto sugere a
seguinte classificação:
• Coleções indexadas
• conjuntos
349
Capítulo 21. Aumentando o des...
• Bags
Todas as coleções indexadas (mapas, listas, matrizes) possuem uma chave primária, que
consiste em colunas <key> e <index>. Neste caso, as atualizações de coleção são geralmente
muito eficientes. A chave primária pode ser indexada de forma eficiente e uma linha em particular
pode ser localizada de forma eficiente quando o Hibernate tentar atualizar ou deletá-la.
Os conjuntos possuem uma chave primária que consiste em <key> e colunas de elemento. Isto
pode ser menos eficiente para alguns tipos de elementos de coleções, especialmente elementos
compostos ou textos grandes ou ainda campos binários. O banco de dados pode não ser capaz de
indexar uma chave primária complexa de forma tão eficiente. Por um outro lado, para associações
um-para-muitos ou muitos-para-muitos, especialmente no caso de identificadores sintáticos, é
bem provável que seja tão eficiente quanto. Se você quiser que o SchemaExport crie para você
uma chave primária de um <set> você deverá declarar todas as colunas como not-null="true".
Os mapeamentos <idbag> definem uma chave substituta, para que elas sejam sempre muito
eficientes ao atualizar. Na verdade, este é o melhor caso.
As Bags são os piores casos. Como uma bag permite duplicar valores de elementos e não
possui coluna de índice, não se deve definir nenhuma chave primária. O Hibernate não tem como
distinguir entre linhas duplicadas. O Hibernate resolve este problema, removendo completamente
em um único DELETE e recria a coleção quando mudar. Isto pode ser bastante ineficiente.
Note que para uma associação um-para-muitos, a chave primária pode não ser a chave primária
física da tabela do banco de dados, mas mesmo neste caso, a classificação acima é ainda útil.
Isto reflete como o Hibernate "localiza" linhas individuais da coleção.
Existe ainda, mais uma vantagem, das coleções indexadas sob conjuntos para associações
muitos-para-muitos. Por causa da estrutura de um Set, o Hibernate nunca utiliza o comando
UPDATE em uma linha quando um elemento é "modificado". As mudanças para o Conjunto
funcionam sempre através do comando INSERT e DELETE de linhas individuais. Novamente, esta
consideração não se aplica às associações um para muitos.
Após observar que as matrizes não podem ser preguiçosas, nós concluimos que as listas, mapas
e bags de id são tipos de coleções com maior desempenho (não inverso), com conjuntos que não
ficam atrás. Espera-se que os conjuntos sejam um tipo mais comum de coleção nas aplicações
Hibernate. Isto porque as semânticas "conjunto" são mais naturais em modelos relacionais.
350
As Bags e listas são as coleções de inversão mais eficientes.
Vamos supor que tenha adicionado um elemento único à uma coleção de tamanho vinte e então
remove dois elementos. O Hibernate irá editar uma instrução INSERT e duas instruções DELETE,
a não ser que a coleção seja uma bag. Isto é certamente desejável.
No entanto, suponha que removamos dezoito elementos, deixando dois e então adicionando três
novos elementos. Existem duas formas possíveis de se proceder:
• delete dezoito linhas uma por uma e então insira três linhas
• remova toda a coleção em um SQL DELETE e insira todos os cinco elementos atuais, um por um
O Hibernate não sabe que a segunda opção é provavelmente mais rápida neste caso. O Hibernate
não deseha saber a opção, uma vez que tal comportamento deve confundir os triggers do banco
de dados, etc.
Felizmente, você pode forçar este comportamento (ou seja, uma segunda estratégia) a qualquer
momento, descartando (ou seja, desreferenciando) a coleção original e retornando uma coleção
recentemente instanciada com todos os elementos atuais.
351
Capítulo 21. Aumentando o des...
É claro que, deletar somente uma vez, não se aplica às coleções mapeadas inverse="true".
Você poderá acessar as métricas da SessionFactory de duas formas. Sua primeira opção é
chamar a sessionFactory.getStatistics() e ler ou dispôr as Estatísticas você mesmo.
O Hibernate também usa o JMX para publicar métricas se você habilitar o MBean de
StatisticsService. Você deve habiliar um MBean único para todas as suas SessionFactory
ou uma por factory. Veja o seguinte código para exemplos de configurações minimalísticos:
352
Métricas
21.6.2. Métricas
O Hibernate oferece um número de métricas, desde informações bem básicas até especializadas,
somente relevantes a certos cenários. Todos os contadores disponíveis estão descritos na API
da interface Statistics, em três categorias:
Por exemplo, você pode verificar a coincidência de um cache, perder e colocar a relação entre
as entidades, colações e consultas e tempo médio que uma consulta precisa. Esteja ciente de
que o número de milisegundos é sujeito a aproximação em Java. O Hibernate é preso à precisão
do JVM, em algumas plataformas a precisão chega a ser de 10 segundos.
Os Getters simples são usados para acessar métricas globais (ou seja, não presos à uma
entidade em particular, coleção, região de cache, etc.) Você pode acessar as métricas de
uma entidade em particular, coleção ou região de cache através de seu nome e através
de sua representação de HQL ou SQL para consultas. Por favor consulte a Javadoc API
Statistics, EntityStatistics, CollectionStatistics, SecondLevelCacheStatistics, e
QueryStatistics para maiores informações. O seguinte código mostra um exemplo simples:
EntityStatistics entityStats =
stats.getEntityStatistics( Cat.class.getName() );
long changes =
entityStats.getInsertCount()
+ entityStats.getUpdateCount()
+ entityStats.getDeleteCount();
log.info(Cat.class.getName() + " changed " + changes + "times" );
Para trabalhar em todas as entidades, coleções, consultas e caches regionais, você poderá
recuperar os nomes de lista de entidades, coleções, consultas e caches regionais com
os seguintes métodos: getQueries(), getEntityNames(), getCollectionRoleNames(), e
getSecondLevelCacheRegionNames().
353
354
Guia de Toolset
É possível realizar uma engenharia de roundtrip com o Hibernate, usando um conjunto de plug-
ins de Eclipse, ferramentas de linha de comando, assim como tarefas Ant.
As Ferramentas do Hibernate atualmente incluem os plug-ins para o IDE de Eclipse assim como
as tarefas para reverter a engenharia dos bancos de dados existentes:
• Console: o console é uma nova visão em Eclipse. Além disso, para uma visão geral de árvore de
suas configurações de console, você também pode obter uma visão interativa de suas classes
persistentes e seus relacionamentos. O console permite que você execute as consultas HQL
junto ao banco de dados e navegue o resultado diretamente em Eclipse.
Por favor, consulte o pacote Ferramentas do Hibernate e suas documentações para maiores
informações.
No entanto, o pacote principal do Hibernate vem em lote com uma ferramenta integrada:
SchemaExport aka hbm2ddl. Ele pode também ser usado dentro do Hibernate.
Primeiro, padronize seus arquivos de mapeamento para melhorar o esquema gerado. A próxima
seção cobrirá a personalização do esquema.
355
Capítulo 22. Guia de Toolset
Algumas tags aceitam uma função not-null para gerar uma restrição NOT NULLnas colunas de
tabela e uma função unique para gerar uma restrição UNIQUE em colunas de tabela.
Uma função unique-key pode ser usada para agrupar colunas em uma restrição de chave única.
Atualmente, o valor específico da função unique-key não é usada para nomear a restrição no
DDL gerado, somente para agrupar as colunas no arquivo de mapeamento.
Uma função index especifica o nome de um indexe que será criado, usando a coluna ou colunas
mapeada(s). As colunas múltiplas podem ser agrupadas no mesmo indexe, simplesmente
especificando o mesmo nome de índice.
Uma função foreign-key pode ser usada para sobrescrever o nome de qualquer restrição de
chave exterior gerada.
356
Padronizando o esquema
A função default deixa você especificar um valor padrão para uma coluna. Você deve atribuir o
mesmo valor à propriedade mapeada antes de salvar uma nova instância da classe mapeada.
357
Capítulo 22. Guia de Toolset
O elemento <comment> permite que você especifique comentários para esquema gerado.
<property name="balance">
<column name="bal">
<comment
>Balance in USD</comment>
</column>
</property
>
358
Executando a ferramenta
Isto resulta em uma instrução comment on table ou comment on column no DDL gerado, onde
é suportado.
Opção Descrição
--quiet não saia do script para stdout
--drop somente suspenda as tabelas
--create somente crie tabelas
--text não exporte para o banco de dados
--output=my_schema.ddl saia do script ddl para um arquivo
--naming=eg.MyNamingStrategy seleciona um NamingStrategy
--config=hibernate.cfg.xml leia a configuração do Hibernate a partir do arquivo
XML
-- leia propriedades de banco de dados a partir dos
properties=hibernate.properties arquivos
--format formatar bem o SQL gerado no script
--delimiter=; ajustar e finalizar o delimitador de linha para o script
22.1.3. Propriedades
As Propriedades do Banco de Daods podem ser especificadas:
359
Capítulo 22. Guia de Toolset
<target name="schemaexport">
<taskdef name="schemaexport"
classname="org.hibernate.tool.hbm2ddl.SchemaExportTask"
classpathref="class.path"/>
<schemaexport
properties="hibernate.properties"
quiet="no"
text="no"
drop="no"
delimiter=";"
output="schema-export.sql">
<fileset dir="src">
<include name="**/*.hbm.xml"/>
</fileset>
</schemaexport>
</target
>
Opção Descrição
--quiet não saia do script para stdout
--text não exporte o script ao banco de dados
--naming=eg.MyNamingStrategy seleciona um NamingStrategy
360
Utilizando Ant para atualizações de esquema incremental
Opção Descrição
-- leia propriedades de banco de dados a partir dos
properties=hibernate.properties arquivos
--config=hibernate.cfg.xml especifique um arquivo .cfg.xml
<target name="schemaupdate">
<taskdef name="schemaupdate"
classname="org.hibernate.tool.hbm2ddl.SchemaUpdateTask"
classpathref="class.path"/>
<schemaupdate
properties="hibernate.properties"
quiet="no">
<fileset dir="src">
<include name="**/*.hbm.xml"/>
</fileset>
</schemaupdate>
</target
>
Opção Descrição
--naming=eg.MyNamingStrategy seleciona um NamingStrategy
361
Capítulo 22. Guia de Toolset
Opção Descrição
-- leia propriedades de banco de dados a partir dos
properties=hibernate.properties arquivos
--config=hibernate.cfg.xml especifique um arquivo .cfg.xml
<target name="schemavalidate">
<taskdef name="schemavalidator"
classname="org.hibernate.tool.hbm2ddl.SchemaValidatorTask"
classpathref="class.path"/>
<schemavalidator
properties="hibernate.properties">
<fileset dir="src">
<include name="**/*.hbm.xml"/>
</fileset>
</schemavalidator>
</target
>
362
Additional modules
Hibernate Core also offers integration with some external modules/projects. This includes
Hibernate Validator the reference implementation of Bean Validation (JSR 303) and Hibernate
Search.
The integration between Hibernate and Bean Validation works at two levels. First, it is able to
check in-memory instances of a class for constraint violations. Second, it can apply the constraints
to the Hibernate metamodel and incorporate them into the generated database schema.
To enable Hibernate's Bean Validation integration, simply add a Bean Validation provider
(preferably Hibernate Validation 4) on your classpath.
23.1.2. Configuration
The Default group is validated on entity insert and update and the database model is updated
accordingly based on the Default group as well.
363
Capítulo 23. Additional modules
You can customize the Bean Validation integration by setting the validation mode. Use
the javax.persistence.validation.mode property and set it up for example in your
persistence.xml file or your hibernate.cfg.xml file. Several options are possible:
• auto (default): enable integration between Bean Validation and Hibernate (callback and ddl
generation) only if Bean Validation is present in the classpath.
• callback: only validate entities when they are either inserted, updated or deleted. An exception
is raised if no Bean Validation provider is present in the classpath.
• ddl: only apply constraints to the database schema when generated by Hibernate. An exception
is raised if no Bean Validation provider is present in the classpath. This value is not defined by
the Java Persistence spec and is specific to Hibernate.
Nota
You can use both callback and ddl together by setting the property to callback,
dll
<persistence ...>
<persistence-unit ...>
...
<properties>
<property name="javax.persistence.validation.mode"
value="callback, ddl"/>
</properties>
</persistence-unit>
</persistence>
If you want to validate different groups during insertion, update and deletion, use:
364
Catching violations
Each property accepts the fully qualified class names of the groups validated separated by a
comma (,)
<persistence ...>
<persistence-unit ...>
...
<properties>
<property name="javax.persistence.validation.group.pre-update"
value="javax.validation.group.Default, com.acme.group.Strict"/>
<property name="javax.persistence.validation.group.pre-remove"
value="com.acme.group.OnDelete"/>
<property name="org.hibernate.validator.group.ddl"
value="com.acme.group.DDL"/>
</properties>
</persistence-unit>
</persistence>
Nota
This exception is wrapped in a RollbackException when the violation happens at commit time.
Otherwise the ConstraintViolationException is returned (for example when calling flush().
Note that generally, catchable violations are validated at a higher level (for example in Seam /
JSF 2 via the JSF - Bean Validation integration or in your business layer by explicitly calling Bean
Validation).
• @NotNull leads to a not null column (unless it conflicts with components or table inheritance)
365
Capítulo 23. Additional modules
• @Digits leads to the definition of precision and scale (ever wondered which is which? It's easy
now with @Digits :) )
These constraints can be declared directly on the entity properties or indirectly by using constraint
composition.
For more information check the Hibernate Validator reference documentation [http://
docs.jboss.org/hibernate/stable/validator/reference/en-US/html/].
366
Exemplo: Pai/Filho
Uma das primeiras coisas que um usuário tenta fazer com o Hibernate é modelar um tipo de
relacionamento Pai/Filho. Existem duas abordagens diferentes para isto. Por diversas razões
diferentes, a abordagem mais conveniente, especialmente para novos usuários, é modelar ambos
os Parent e Child como classes de entidade com uma associação <one-to-many> a partir
do Parent para o Child. A abordagem alternativa é declarar o Child como um <composite-
element>. As semânticas padrões da associação um para muitos (no Hibernate), são muito
menos parecidas com as semânticas comuns de um relacionamento pai/filho do que aqueles
de um mapeamento de elemento de composição. Explicaremos como utilizar uma associação
bidirecional um para muitos com cascatas para modelar um relacionamento pai/filho de forma
eficiente e elegante.
• Se um objeto removido de uma coleção for uma instância de um tipo de valor (ex.: um elemento
de composição), este objeto irá parar de ser persistente e seu estado será completamente
removido do banco de dados. Da mesma forma, ao adicionar uma instância de tipo de valor à
coleção, causará ao estado uma persistência imediata.
• Por outro lado, se uma entidade é removida de uma coleção (uma associação um-para-
muitos ou muitos-para-muitos), ela não será deletada por padrão. Este comportamento é
completamente consistente, uma mudança para o estado interno de uma outra entidade não
deve fazer com que a entidade associada desapareça. Da mesma forma, ao adicionar uma
entidade à coleção, não faz com que a entidade se torne persistente, por padrão.
A adição de uma entidade à coleção, por padrão, meramente cria um link entre as duas entidades.
A remoção da entidade, removerá o link. Isto é muito apropriado para alguns tipos de casos. No
entanto, não é apropriado o caso de um relacionamento pai/filho. Neste caso, a vida do filho está
vinculada ao ciclo de vida do pai.
<set name="children">
<key column="parent_id"/>
367
Capítulo 24. Exemplo: Pai/Filho
<one-to-many class="Child"/>
</set
>
Parent p = .....;
Child c = new Child();
p.getChildren().add(c);
session.save(c);
session.flush();
Isto não é somente ineficiente como também viola qualquer restrição NOT NULL na coluna
parent_id. Nós podemos concertar a violação da restrição de nulabilidade, especificando um
not-null="true" no mapeamento da coleção:
<set name="children">
<key column="parent_id" not-null="true"/>
<one-to-many class="Child"/>
</set
>
As causas subjacentes deste comportamento é que o link (a chave exterior parent_id) de p para
c não é considerada parte do estado do objeto Child e por isso não é criada no INSERT. Então
a solução é fazer uma parte de link do mapeamento do Child.
Agora que a entidade Child está gerenciando o estado do link, informaremos à coleção para não
atualizar o link. Utilizamos o atributo inverse para isto:
368
Ciclo de vida em Cascata
</set
>
369
Capítulo 24. Exemplo: Pai/Filho
session.flush();
Da mesma forma, não precisamos repetir este comando com os filhos ao salvar ou deletar um
Parent. O comando seguinte irá remover o p e todos os seus filhos do banco de dados.
não irá remover c do banco de dados. Neste caso, ele somente removerá o link para p e causará
uma violação de restrição NOT NULL). Você precisará delete() de forma explícita o Child.
Agora, no seu caso, um Child não pode existir sem seu pai. Então, se removermos um Child
da coleção, não iremos mais querer que ele seja deletado. Devido a isto, devemos utilizar um
cascade="all-delete-orphan".
370
Cascatas e unsaved-value
Bem, isto cabe bem no caso de um identificador gerado, mas e os identificadores atribuídos e os
identificadores de composição? Isto é mais difícil, pois uma vez que o Hibernate não pode utilizar
a propriedade do identificador para distinguir entre um objeto instanciado recentemente, com um
identificador atribuído pelo usuário, e um objeto carregado em uma sessão anterior. Neste caso,
o Hibernate usará tanto um carimbo de data e hora (timestamp) ou uma propriedade de versão,
ou irá na verdade consultar um cache de segundo nível, ou no pior dos casos, o banco de dados,
para ver se a linha existe.
24.5. Conclusão
Há muito o que digerir aqui e pode parecer confuso na primeira vez. No entanto, na prática,
funciona muito bem. A maioria dos aplicativos do Hibernate utiliza o modelo pai/filho em muitos
lugares.
Nós mencionamos uma alternativa neste primeiro parágrafo. Nenhum dos casos acima existem
no caso de mapeamentos <composite-element>, que possuem exatamente a semântica
do relacionamento pai/filho. Infelizmente, existem duas grandes limitações para elementos
compostos: elementos compostos podem não possuir coleções e assim sendo podem não ser
filhos de nenhuma outra entidade a não ser do pai único.
371
372
Exemplo: Aplicativo Weblog
25.1. Classes Persistentes
As classes persistentes representam um weblog, e um item postado em um weblog. Eles não
devem ser modelados como um relacionamento padrão pai/filho, mas usaremos uma bolsa
ordenada ao invés de um conjunto:
package eg;
import java.util.List;
package eg;
import java.text.DateFormat;
import java.util.Calendar;
373
Capítulo 25. Exemplo: Aplicat...
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="eg">
<class
name="Blog"
table="BLOGS">
<id
name="id"
column="BLOG_ID">
<generator class="native"/>
</id>
<property
name="name"
column="NAME"
not-null="true"
374
Mapeamentos Hibernate
unique="true"/>
<bag
name="items"
inverse="true"
order-by="DATE_TIME"
cascade="all">
<key column="BLOG_ID"/>
<one-to-many class="BlogItem"/>
</bag>
</class>
</hibernate-mapping
>
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="eg">
<class
name="BlogItem"
table="BLOG_ITEMS"
dynamic-update="true">
<id
name="id"
column="BLOG_ITEM_ID">
<generator class="native"/>
</id>
<property
name="title"
column="TITLE"
not-null="true"/>
<property
name="text"
column="TEXT"
not-null="true"/>
<property
name="datetime"
column="DATE_TIME"
not-null="true"/>
<many-to-one
name="blog"
column="BLOG_ID"
not-null="true"/>
375
Capítulo 25. Exemplo: Aplicat...
</class>
</hibernate-mapping
>
package eg;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.tool.hbm2ddl.SchemaExport;
376
Código Hibernate
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return blog;
}
377
Capítulo 25. Exemplo: Aplicat...
}
finally {
session.close();
}
return item;
}
item.setText(text);
378
Código Hibernate
379
Capítulo 25. Exemplo: Aplicat...
result = q.list();
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return result;
}
}
380
Exemplo: Vários Mapeamentos
Este capítulo mostra algums mapeamentos de associações mais complexos.
26.1. Empregador/Empregado
O modelo a seguir, do relacionamento entre Employer e Employee utiliza uma entidade de classe
atual (Employment) para representar a associação. Isto é feito porque pode-se ter mais do que
um período de trabalho para as duas partes envolvidas. Outros Componentes são usados para
modelar valores monetários e os nomes do empregado.
<hibernate-mapping>
<id name="id">
<generator class="sequence">
<param name="sequence"
>employment_id_seq</param>
</generator>
</id>
<property name="startDate" column="start_date"/>
<property name="endDate" column="end_date"/>
381
Capítulo 26. Exemplo: Vários ...
</class>
</hibernate-mapping
>
382
Autor/Trabalho
26.2. Autor/Trabalho
Considere o seguinte modelo de relacionamento entre Work, Author e Person. Nós
representamos o relacionamento entre Work e Author como uma associação muitos-para-muitos.
Nós escolhemos representar o relacionamento entre Author e Person como uma associação
um-para-um. Outra possibilidade seria ter Author estendendo Person.
<hibernate-mapping>
<property name="title"/>
<set name="authors" table="author_work">
<key column name="work_id"/>
<many-to-many class="Author" column name="author_id"/>
</set>
383
Capítulo 26. Exemplo: Vários ...
</class>
<property name="alias"/>
<one-to-one name="person" constrained="true"/>
</class>
</hibernate-mapping
>
Existem quatro tabelas neste mapeamento: works, authors e persons matém os dados de
trabalho, autor e pessoa, respectivamente. O author_work é uma tabela de associação que liga
autores à trabalhos. Abaixo, segue o esquema das tabelas, gerados pelo SchemaExport:
384
Cliente/Ordem/Produto
26.3. Cliente/Ordem/Produto
Agora considere um modelo de relacionamento entre Customer, Order e LineItem e Product.
Existe uma associação um-para-muitos entre Customer e Order, mas como devemos representar
Order / LineItem / Product? Neste exemplo, o LineItem é mapeado como uma classe
de associação representando a associação muitos-para-muitos entre Order e Product. No
Hibernate, isto é conhecido como um elemento composto.
<hibernate-mapping>
385
Capítulo 26. Exemplo: Vários ...
</hibernate-mapping
>
386
Exemplos variados de mapeamento
<class name="Person">
<id name="name"/>
<one-to-one name="address"
cascade="all">
<formula
>name</formula>
<formula
>'HOME'</formula>
</one-to-one>
<one-to-one name="mailingAddress"
cascade="all">
<formula
>name</formula>
<formula
>'MAILING'</formula>
</one-to-one>
</class>
387
Capítulo 26. Exemplo: Vários ...
<class name="Customer">
<id name="customerId"
length="10">
<generator class="assigned"/>
</id>
<list name="orders"
inverse="true"
cascade="save-update">
<key column="customerId"/>
<index column="orderNumber"/>
<one-to-many class="Order"/>
</list>
</class>
<composite-id name="id"
class="Order$Id">
<key-property name="customerId" length="10"/>
<key-property name="orderNumber"/>
</composite-id>
<property name="orderDate"
type="calendar_date"
not-null="true"/>
<property name="total">
<formula>
( select sum(li.quantity*p.price)
from LineItem li, Product p
where li.productId = p.productId
and li.customerId = customerId
and li.orderNumber = orderNumber )
</formula>
</property>
<many-to-one name="customer"
column="customerId"
insert="false"
update="false"
not-null="true"/>
<bag name="lineItems"
fetch="join"
inverse="true"
cascade="save-update">
<key>
388
Exemplo de chave composta
<column name="customerId"/>
<column name="orderNumber"/>
</key>
<one-to-many class="LineItem"/>
</bag>
</class>
<class name="LineItem">
<composite-id name="id"
class="LineItem$Id">
<key-property name="customerId" length="10"/>
<key-property name="orderNumber"/>
<key-property name="productId" length="10"/>
</composite-id>
<property name="quantity"/>
<many-to-one name="order"
insert="false"
update="false"
not-null="true">
<column name="customerId"/>
<column name="orderNumber"/>
</many-to-one>
<many-to-one name="product"
insert="false"
update="false"
not-null="true"
column="productId"/>
</class>
<class name="Product">
<synchronize table="LineItem"/>
<id name="productId"
length="10">
<generator class="assigned"/>
</id>
<property name="description"
not-null="true"
length="200"/>
<property name="price" length="3"/>
<property name="numberAvailable"/>
<property name="numberOrdered">
<formula>
( select sum(li.quantity)
from LineItem li
where li.productId = productId )
</formula>
</property>
</class
389
Capítulo 26. Exemplo: Vários ...
>
<class name="Person"
discriminator-value="P">
<id name="id"
column="person_id"
unsaved-value="0">
<generator class="native"/>
</id>
390
Associações em chaves alternativas
<discriminator
type="character">
<formula>
case
when title is not null then 'E'
when salesperson is not null then 'C'
else 'P'
end
</formula>
</discriminator>
<property name="name"
not-null="true"
length="80"/>
<property name="sex"
not-null="true"
update="false"/>
<component name="address">
<property name="address"/>
<property name="zip"/>
<property name="country"/>
</component>
<subclass name="Employee"
discriminator-value="E">
<property name="title"
length="20"/>
<property name="salary"/>
<many-to-one name="manager"/>
</subclass>
<subclass name="Customer"
discriminator-value="C">
<property name="comments"/>
<many-to-one name="salesperson"/>
</subclass>
</class
>
<class name="Person">
<id name="id">
<generator class="hilo"/>
</id>
<one-to-one name="address"
property-ref="person"
391
Capítulo 26. Exemplo: Vários ...
cascade="all"
fetch="join"/>
<set name="accounts"
inverse="true">
<key column="userId"
property-ref="userId"/>
<one-to-many class="Account"/>
</set>
</class>
<class name="Address">
<id name="id">
<generator class="hilo"/>
</id>
</class>
<class name="Account">
<id name="accountId" length="32">
<generator class="uuid"/>
</id>
<many-to-one name="user"
column="userId"
property-ref="userId"/>
</class
>
392
Melhores práticas
Escreva classes compactas e mapeie-as usando <component>:
Use uma classe Endereço para encapsular rua, bairro, estado, CEP. Isto promove a
reutilização de código e simplifica o refactoring.
393
Capítulo 27. Melhores práticas
suponha que o uso direto do JDBC é necessariamente mais rápido. Se você precisar usar
diretamente o JDBC, vale a pena abrir uma Session do Hibernate, embrulhar a sua operaçäo
JDBC como um objeto org.hibernate.jdbc.Work e usar uma conexão JDBC. De modo que
você possa ainda usar a mesma estratégia de transação e ocultar o provedor a conexão.
394
Use o modelo sessão aberta na visualização, ou uma fase de construção para evitar problemas
com dados não encontrados.
O Hibernate libera o desenvolvedor de escrever Objetos de Transferência de Dados (DTO).
Em uma arquitetura tradicional EJB, os DTOs servem dois propósitos: primeiro, eles se
deparam com o problema de que os beans de entidade não são serializáveis, depois, eles
implicitamente definem uma fase de construção onde todos os dados a serem utilizados pelo
view são buscados e conduzidos aos DTOs antes mesmo de retornar o controle à camada
de apresentação. O Hibernate elimina o primeiro propósito. No entanto, você ainda precisará
de uma fase de construção (pense em seus métodos de negócios como tendo um contrato
estrito com a camada de apresentação sobre o quais dados estão disponíveis nos objetos
desanexados) a não ser que você esteja preparado para manter o contexto de persistência
(sessão) aberto no processo de renderização da visualização. Isto não é uma limitação do
Hibernate. É uma solicitação fundamental para acesso a dados transacionais seguros.
395
396
Considerações da Portabilidade do
Banco de Dados
28.1. Fundamentos da Portabilidade
Um dos pontos de venda do Hibernate (e realmente Mapeamento do Objeto/Relacional como um
conjunto) é a noção da portabilidade do banco de dados. Isto pode significar um usuário de TI
interno migrando a partir de um fornecedor de banco de dados a outro, ou isto pode significar que
um framework ou aplicativo implementável consumindo o Hibernate para produtos de banco de
dados múltiplos de destinação simultaneamente pelos usuários. Independente do cenário exato,
a idéia básica é que você queira que o Hibernate o ajude a rodar em referência a qualquer número
de banco de dados sem as alterações a seu código e preferencialmente sem quaisquer alterações
ao metadados de mapeamento.
28.2. Dialeto
A primeira linha de portabilidade para o Hibernate é o dialeto, que trata-se de uma especialização
de um contrato org.hibernate.dialect.Dialect. Um dialeto encapsula todas as diferenças em
como o Hibernate deve comunicar-se com um banco de dados particular para completar algumas
tarefas como obter um valor de seqüência ou estruturar uma consulta SELECT. O Hibernate
vincula uma variedade de dialetos para muitos dos bancos de dados mais populares. Se você
achar que seu banco de dados particular não está seguindo os mesmos, não será difícil escrever
o seu próprio.
Inicializando com a versão 3.2, o Hibernate introduziu a noção de detecção automática do dialeto
para uso baseado no java.sql.DatabaseMetaData obtido a partir de um java.sql.Connection
para aquele banco de dados. Era muito melhor, esperar que esta resolução limitada aos bancos
de dados Hibernate soubesse com antecedência e que em ocasião alguma era configurável ou
substituível.
Starting with version 3.3, Hibernate has a fare more powerful way to automatically determine
which dialect to should be used by relying on a series of delegates which implement the
org.hibernate.dialect.resolver.DialectResolver which defines only a single method:
397
Capítulo 28. Considerações da...
The basic contract here is that if the resolver 'understands' the given database metadata then
it returns the corresponding Dialect; if not it returns null and the process continues to the next
resolver. The signature also identifies org.hibernate.exception.JDBCConnectionException
as possibly being thrown. A JDBCConnectionException here is interpreted to imply a "non
transient" (aka non-recoverable) connection problem and is used to indicate an immediate stop to
resolution attempts. All other exceptions result in a warning and continuing on to the next resolver.
Nota
Hibernate was changed slightly once the implication of this was better understood
so that the insert is delayed in cases where that is feasible.
The underlying issue is that the actual semanctics of the application itself changes in these cases.
Starting with version 3.2.3, Hibernate comes with a set of enhanced [http://in.relation.to/2082.lace]
identifier generators targetting portability in a much different way.
398
Funções do banco de dados
Nota
There are specifically 2 bundled enhancedgenerators:
• org.hibernate.id.enhanced.SequenceStyleGenerator
• org.hibernate.id.enhanced.TableGenerator
Atenção
Esta é uma área do Hibernate com necessidade de melhoramentos. Este
manuseio de função funciona atualmente muito bem com o HQL, quando falamos
das preocupações de portabilidade. No entanto, é bastante precária em outros
aspectos.
As funções SQL podem ser referenciadas em diversas maneiras pelos usuários. No entanto,
nem todos os bancos de dados suportam o mesmo conjunto de função. O Hibernate fornece
um significado de mapeamento do nome da função lógica para uma delegação que sabe como
manusear aquela função em particular, mesmo quando usando uma chamada de função física
totalmente diferente.
Importante
Technically this function registration is handled through the
org.hibernate.dialect.function.SQLFunctionRegistry class which is
intended to allow users to provide custom function definitions without having to
provide a custom dialect. This specific behavior is not fully completed as of yet.
It is sort of implemented such that users can programatically register functions with
the org.hibernate.cfg.Configuration and those functions will be recognized
for HQL.
399
400
Referências
[PoEAA] Padröes da Arquitetura do Aplicativo Enterprise . 0-321-12742-0. por Martin Fowler.
Copyright © 2003 Pearson Education, Inc.. Addison-Wesley Publishing Company.
[JPwH] Persitência Java com Hibernate. Segunda Edição do Hibernate em Ação. 1-932394-88-5.
http://www.manning.com/bauer2 . por Christian Bauer e Gavin King. Copyright © 2007
Manning Publications Co.. Manning Publications Co..
401
402