Escolar Documentos
Profissional Documentos
Cultura Documentos
ndice
Prefcio ........................................................................................................................................ viii 1. Introduo ao Hibernate ............................................................................................................. 1 1.1. Prefcio ............................................................................................................................. 1 1.2. Parte 1 A primeira aplicao Hibernate ............................................................................. 1 1.2.1. A primeira Classe .................................................................................................... 1 1.2.2. O mapeamento do arquivo ....................................................................................... 3 1.2.3. Configurao do Hibernate ....................................................................................... 5 1.2.4. Construindo com o Ant ............................................................................................ 6 1.2.5. Startup and helpers .................................................................................................. 7 1.2.6. Carregando e salvando objetos ................................................................................. 8 1.3. Parte 2 - Mapeando associaes ........................................................................................ 10 1.3.1. Mapeando a classe Person ...................................................................................... 10 1.3.2. Uma associao Set-based unidirectional ................................................................ 11 1.3.3. Trabalhando a associao ....................................................................................... 12 1.3.4. Coleo de valores ................................................................................................. 14 1.3.5. Associaes bidirecionais ...................................................................................... 15 1.3.6. Trabalhando com links bidirecionais ....................................................................... 16 1.4. EventManager um aplicativo para internet ......................................................................... 16 1.4.1. Criando um servlet bsico ...................................................................................... 17 1.4.2. Processando e renderizando ................................................................................... 17 1.4.3. Instalando e testando ............................................................................................. 19 1.5. Sumrio ........................................................................................................................... 20 2. Arquitetura ............................................................................................................................... 21 2.1. Viso Geral ...................................................................................................................... 21 2.2. Estados de instncia ......................................................................................................... 23 2.3. Integrao JMX ............................................................................................................... 23 2.4. Suporte JCA .................................................................................................................... 24 2.5. Sesses contextuais .......................................................................................................... 24 3. Configurao ............................................................................................................................. 26 3.1. 1.11 Configurao programtica ........................................................................................ 26 3.2. Obtendo uma SessionFactory ............................................................................................ 27 3.3. Conexes JDBC ............................................................................................................... 27 3.4. Propriedades opcionais de configurao ............................................................................. 28 3.4.1. Dialetos SQL ........................................................................................................ 34 3.4.2. Recuperao por unio externa (Outer Join Fetching) .............................................. 35 3.4.3. Fluxos Binrios (Binary Streams) ........................................................................... 35 3.4.4. Cach de segundo nvel e query .............................................................................. 35 3.4.5. Substituies na Linguagem de Consulta ................................................................. 36 3.4.6. Estatsticas do Hibernate ........................................................................................ 36 3.5. Logging ........................................................................................................................... 36 3.6. Implementado uma NamingStrategy .................................................................................. 37 3.7. Arquivo de configurao XML ......................................................................................... 37 3.8. Integrao com servidores de aplicao J2EE ..................................................................... 38 3.8.1. Configurao de estratgia de transao .................................................................. 39 3.8.2. SessionFactory ligada a JNDI ................................................................................. 40 3.8.3. Current Session context management with JTA ....................................................... 40 3.8.4. JMX deployment ................................................................................................... 41 4. Persistent Classes ...................................................................................................................... 43 Hibernate 3.2 cr2 ii
HIBERNATE - Relational Persistence for Idiomatic Java 4.1. A simple POJO example ................................................................................................... 43 4.1.1. Implement a no-argument constructor ..................................................................... 44 4.1.2. Provide an identifier property (optional) .................................................................. 44 4.1.3. Prefer non-final classes (optional) ........................................................................... 44 4.1.4. Declare accessors and mutators for persistent fields (optional) .................................. 45 4.2. Implementing inheritance ................................................................................................. 45 4.3. Implementing equals() and hashCode() .............................................................................. 45 4.4. Dynamic models .............................................................................................................. 46 4.5. Tuplizers ......................................................................................................................... 48 5. Mapeamento O/R Bassico ......................................................................................................... 50 5.1. Declarao de mapeamento ............................................................................................... 50 5.1.1. Doctype ................................................................................................................ 51 5.1.1.1. EntityResolver ............................................................................................ 51 5.1.2. hibernate-mapping ................................................................................................. 52 5.1.3. class ..................................................................................................................... 52 5.1.4. id .......................................................................................................................... 55 5.1.4.1. Generator ................................................................................................... 56 5.1.4.2. Algoritmo Hi/lo .......................................................................................... 57 5.1.4.3. UUID algorithm ......................................................................................... 57 5.1.4.4. Colunas de identidade e sequencias .............................................................. 57 5.1.4.5. Identificadores especificados ....................................................................... 58 5.1.4.6. Chaves primrias geradas por triggers .......................................................... 58 5.1.5. composite-id ......................................................................................................... 58 5.1.6. discriminator ......................................................................................................... 59 5.1.7. version (optional) .................................................................................................. 60 5.1.8. timestamp (optional) .............................................................................................. 61 5.1.9. property ................................................................................................................ 61 5.1.10. many-to-one ........................................................................................................ 63 5.1.11. one-to-one (um-pra-um) ....................................................................................... 65 5.1.12. natural-id ............................................................................................................ 66 5.1.13. componente, componente dinmico. ..................................................................... 67 5.1.14. propriedades ........................................................................................................ 67 5.1.15. subclass (subclasse) ............................................................................................. 68 5.1.16. joined-subclass .................................................................................................... 69 5.1.17. union-subclass ..................................................................................................... 70 5.1.18. join ..................................................................................................................... 71 5.1.19. key ..................................................................................................................... 71 5.1.20. elementos column e formula ................................................................................. 72 5.1.21. import ................................................................................................................. 73 5.1.22. any ..................................................................................................................... 73 5.2. Tipos do Hibernate ........................................................................................................... 74 5.2.1. Entidades e valores ................................................................................................ 74 5.2.2. Valores de tipos bsicos ......................................................................................... 75 5.2.3. Tipos de valores personalizados ............................................................................. 76 5.3. Mapping a class more than once ........................................................................................ 77 5.4. SQL quoted identifiers ...................................................................................................... 77 5.5. Metadata alternatives ........................................................................................................ 77 5.5.1. Using XDoclet markup .......................................................................................... 78 5.5.2. Using JDK 5.0 Annotations .................................................................................... 79 5.6. Generated Properties ........................................................................................................ 80 5.7. Auxiliary Database Objects ............................................................................................... 80 6. Mapeamento de Colees. ......................................................................................................... 82
iii
HIBERNATE - Relational Persistence for Idiomatic Java 6.1. Persistent collections ........................................................................................................ 82 6.2. Collection mappings ......................................................................................................... 83 6.2.1. Collection foreign keys .......................................................................................... 84 6.2.2. Collection elements ............................................................................................... 84 6.2.3. Indexed collections ................................................................................................ 85 6.2.4. Collections of values and many-to-many associations .............................................. 85 6.2.5. One-to-many associations ...................................................................................... 87 6.3. Advanced collection mappings .......................................................................................... 88 6.3.1. Sorted collections .................................................................................................. 88 6.3.2. Bidirectional associations ....................................................................................... 89 6.3.3. Associaes bidirecionais com colees indexadas .................................................. 90 6.3.4. Associaes Ternrias ............................................................................................ 91 6.3.5. Usando o <idbag> ................................................................................................. 91 6.4. Exemplos de colees ....................................................................................................... 92 7. Mapeamento de Associaes. ..................................................................................................... 95 7.1. Introduo ....................................................................................................................... 95 7.2. Associaes Unidirecionais ............................................................................................... 95 7.2.1. muitos para um ...................................................................................................... 95 7.2.2. um para um ........................................................................................................... 95 7.2.3. um para muitos ...................................................................................................... 96 7.3. Associaes Unidirecionais com tabelas associativas .......................................................... 97 7.3.1. um para muitos ...................................................................................................... 97 7.3.2. muitos para um ...................................................................................................... 97 7.3.3. um para um ........................................................................................................... 98 7.3.4. muitos para muitos ................................................................................................ 98 7.4. Associaes Bidirecionais ................................................................................................. 99 7.4.1. um para muitos / muitos para um ............................................................................ 99 7.4.2. um para um ......................................................................................................... 100 7.5. Associaes Bidirecionais com tabelas associativas .......................................................... 100 7.5.1. um para muitos / muitos para um .......................................................................... 100 7.5.2. one to one ........................................................................................................... 101 7.5.3. muitos para muitos .............................................................................................. 102 7.6. Mapeamento de associaes mais complexas ................................................................... 102 8. Mapeamento de Componentes. ................................................................................................ 104 8.1. Objetos dependentes ....................................................................................................... 104 8.2. Collections de objetos dependentes .................................................................................. 105 8.3. Componentes como ndices de Map ................................................................................. 106 8.4. . Componentes como identificadores compostos ............................................................... 107 8.5. Componentes Dinmicos ................................................................................................ 108 9. Mapeamento de Herana ........................................................................................................ 109 9.1. As trs estratgias .......................................................................................................... 109 9.1.1. Tabela por hierarquia de classes ........................................................................... 109 9.1.2. Tabela por subclasse ............................................................................................ 110 9.1.3. Tabela por subclasse, usando um discriminador ..................................................... 110 9.1.4. . Misturando tabela por hierarquia de classes com tabela por subclasse ................... 111 9.1.5. Tabela por classe concreta .................................................................................... 111 9.1.6. Tabela por classe concreta, usando polimorfismo implcito .................................... 112 9.1.7. Misturando polimorfismo implcito com outros mapeamentos de herana ............... 113 9.2. Limitaes ..................................................................................................................... 113 10. Trabalhando com objetos ...................................................................................................... 115 10.1. Estado dos objetos no Hibernate .................................................................................... 115 10.2. Tornando os objetos persistentes ................................................................................... 115
iv
HIBERNATE - Relational Persistence for Idiomatic Java 10.3. Carregando o objetos .................................................................................................... 116 10.4. Consultando ................................................................................................................. 117 10.4.1. Executando consultas ......................................................................................... 117 10.4.1.1. Interagindo com resultados ...................................................................... 118 10.4.1.2. Consultas que retornam tuplas ................................................................. 118 10.4.1.3. Resultados escalares ................................................................................ 118 10.4.1.4. Bind parameters ...................................................................................... 119 10.4.1.5. Pagination .............................................................................................. 119 10.4.1.6. Scrollable iteration .................................................................................. 119 10.4.1.7. Externalizing named queries .................................................................... 120 10.4.2. Filtering collections ........................................................................................... 120 10.4.3. Criteria queries .................................................................................................. 121 10.4.4. Queries in native SQL ........................................................................................ 121 10.5. Modifying persistent objects ......................................................................................... 121 10.6. Modifying detached objects .......................................................................................... 122 10.7. Automatic state detection .............................................................................................. 123 10.8. Deleting persistent objects ............................................................................................ 124 10.9. Replicating object between two different datastores ........................................................ 124 10.10. Flushing the Session ................................................................................................... 124 10.11. Transitive persistence ................................................................................................. 125 10.12. Usando metadados ...................................................................................................... 126 11. Transaes e Concorrncia .................................................................................................... 128 11.1. Session e escopos de transaes .................................................................................... 128 11.1.1. Unidade de trabalho ........................................................................................... 128 11.1.2. Longas conversaes ......................................................................................... 129 11.1.3. Considerando a identidade do objeto ................................................................... 130 11.1.4. Edies comuns ................................................................................................. 131 11.2. Demarcao de transaes de bancos de dados ............................................................... 131 11.2.1. Ambiente no gerenciado ................................................................................... 132 11.2.2. Usando JTA ...................................................................................................... 133 11.2.3. Tratamento de Exceo ...................................................................................... 134 11.2.4. Timeout de Transao ........................................................................................ 135 11.3. Controle de concorrncia otimista .................................................................................. 135 11.3.1. Checagem de verso da aplicao ....................................................................... 136 11.3.2. Sesso estendida e versionamento automtico ...................................................... 136 11.3.3. Objetos destacados e versionamento automtico .................................................. 137 11.3.4. Versionamento automtico customizado .............................................................. 138 11.4. Locking pessimista ....................................................................................................... 138 11.5. Modos de liberar a Connection ...................................................................................... 139 12. Interceptadores e Eventos ..................................................................................................... 141 12.1. Interceptadores ............................................................................................................. 141 12.2. Sistema de Eventos ....................................................................................................... 142 12.3. Hibernate declarative security ....................................................................................... 143 13. Processamento de lotes .......................................................................................................... 145 13.1. Insero de lotes ........................................................................................................... 145 13.2. Batch updates ............................................................................................................... 145 13.3. A interface StatelessSession .......................................................................................... 146 13.4. Operaes no estilo DML ............................................................................................. 146 14. HQL: A linguagem de Queries do Hibernate ......................................................................... 149 14.1. Case Sensitve .............................................................................................................. 149 14.2. A clausula from ............................................................................................................ 149 14.3. Associaes e joins ....................................................................................................... 149
HIBERNATE - Relational Persistence for Idiomatic Java 14.4. Formas e sintaxe de joins .............................................................................................. 151 14.5. Clausula select ............................................................................................................. 151 14.6. Funes de agregao ................................................................................................... 152 14.7. Queries polimrficas .................................................................................................... 152 14.8. A clausula where .......................................................................................................... 153 14.9. Expresses ................................................................................................................... 154 14.10. A clausula order by ..................................................................................................... 157 14.11. A clausula group by .................................................................................................... 157 14.12. Subqueries ................................................................................................................. 158 14.13. Exemplos de HQL ...................................................................................................... 159 14.14. update e delete em lote ................................................................................................ 160 14.15. Dicas e Truques .......................................................................................................... 160 15. Consultas por critrios .......................................................................................................... 162 15.1. Criando uma instancia Criteria ...................................................................................... 162 15.2. Limitando o result set ................................................................................................... 162 15.3. Ordering the results ...................................................................................................... 163 15.4. Associations ................................................................................................................. 163 15.5. Dynamic association fetching ........................................................................................ 164 15.6. Example queries ........................................................................................................... 164 15.7. Projections, aggregation and grouping ........................................................................... 165 15.8. Detached queries and subqueries ................................................................................... 166 15.9. Queries by natural identifier .......................................................................................... 166 16. Native SQL ............................................................................................................................ 168 16.1. Using a SQLQuery ....................................................................................................... 168 16.1.1. Scalar queries .................................................................................................... 168 16.1.2. Entity queries .................................................................................................... 169 16.1.3. Handling associations and collections ................................................................. 169 16.1.4. Returning multiple entities ................................................................................. 169 16.1.4.1. Alias and property references ................................................................... 170 16.1.5. Returning non-managed entities .......................................................................... 171 16.1.6. Handling inheritance .......................................................................................... 171 16.1.7. Parameters ........................................................................................................ 171 16.2. Named SQL queries ..................................................................................................... 172 16.2.1. Using return-property to explicitly specify column/alias names ............................. 173 16.2.2. Using stored procedures for querying .................................................................. 174 16.2.2.1. Rules/limitations for using stored procedures ............................................ 174 16.3. SQL customizado para create, update e delete ................................................................ 175 16.4. SQL customizado para carga ......................................................................................... 176 17. Filtrando dados ..................................................................................................................... 178 17.1. Filtros do Hibernate ...................................................................................................... 178 18. Mapeamento XML ................................................................................................................ 180 18.1. Trabalhando com dados em XML .................................................................................. 180 18.1.1. Especificando o mapeamento de uma classe e de um arquivo XML simultaneamente 180 18.1.2. Especificando somente um mapeamento XML .................................................... 180 18.2. Mapeando metadados com XML ................................................................................... 181 18.3. Manipulando dados em XML ........................................................................................ 182 19. Aumentando a performance .................................................................................................. 184 19.1. Estratgias de Fetching ................................................................................................. 184 19.1.1. Trabalhando com associaes preguiosas (lazy) ................................................. 185 19.1.2. Personalizando as estratgias de busca ................................................................ 185 19.1.3. Proxies de associao single-ended ..................................................................... 186 19.1.4. Inicializando colees e proxies .......................................................................... 187
vi
HIBERNATE - Relational Persistence for Idiomatic Java 19.1.5. Usando busca em lote ........................................................................................ 189 19.1.6. Usando subselect fetching .................................................................................. 189 19.1.7. Usando busca preguiosa de propriedade ............................................................. 189 19.2. The Second Level Cache ............................................................................................... 190 19.2.1. Cache mappings ................................................................................................ 191 19.2.2. Strategy: read only ............................................................................................. 191 19.2.3. Strategy: read/write ............................................................................................ 191 19.2.4. Strategy: nonstrict read/write .............................................................................. 192 19.2.5. Strategy: transactional ........................................................................................ 192 19.3. Managing the caches .................................................................................................... 192 19.4. The Query Cache ......................................................................................................... 193 19.5. Understanding Collection performance .......................................................................... 194 19.5.1. Taxonomy ......................................................................................................... 194 19.5.2. Lists, maps, idbags and sets are the most efficient collections to update ................. 195 19.5.3. Bags and lists are the most efficient inverse collections ........................................ 195 19.5.4. One shot delete .................................................................................................. 195 19.6. Monitoring performance ............................................................................................... 196 19.6.1. Monitoring a SessionFactory .............................................................................. 196 19.6.2. Metrics ............................................................................................................. 197 20. Toolset Guide ........................................................................................................................ 199 20.1. Automatic schema generation ........................................................................................ 199 20.1.1. Customizing the schema ..................................................................................... 199 20.1.2. Running the tool ................................................................................................ 201 20.1.3. Properties .......................................................................................................... 202 20.1.4. Using Ant ......................................................................................................... 202 20.1.5. Incremental schema updates ............................................................................... 203 20.1.6. Using Ant for incremental schema updates .......................................................... 203 20.1.7. Schema validation ............................................................................................. 203 20.1.8. Using Ant for schema validation ......................................................................... 204 21. Example: Parent/Child .......................................................................................................... 205 21.1. A note about collections ................................................................................................ 205 21.2. Bidirectional one-to-many ............................................................................................. 205 21.3. Cascading lifecycle ....................................................................................................... 206 21.4. Cascades and unsaved-value ......................................................................................... 207 21.5. Conclusion ................................................................................................................... 208 22. Example: Weblog Application ............................................................................................... 209 22.1. Persistent Classes ......................................................................................................... 209 22.2. Hibernate Mappings ..................................................................................................... 210 22.3. Hibernate Code ............................................................................................................ 211 23. Exemplo: Vrios Mapeamentos ............................................................................................. 215 23.1. Employer/Employee ..................................................................................................... 215 23.2. Author/Work ................................................................................................................ 216 23.3. Customer/Order/Product ............................................................................................... 218 23.4. Exemplos variados de mapeamento ............................................................................... 220 23.4.1. Associao um-para-um "Tipadas" ..................................................................... 220 23.4.2. Exemplo de chave composta ............................................................................... 220 23.4.3. Muitos-para-muitos com atributo de chave composta compartilhada ..................... 222 23.4.4. Contedo baseado em descriminao .................................................................. 223 23.4.5. Associaes em chaves alternativas .................................................................... 223 24. Boas prticas ......................................................................................................................... 225
vii
Prefcio
Advertncia! Esta uma verso traduzida do ingls da documentao de referencia do Hibernate. A verso traduzida pode estar desatualizada. Sem dvida, as diferenas devem ser pequenas e sero corrigidas o mais breve possvel. Consulte a documentao de referencia em ingls, se estiver faltando alguma informao ou voc encontrar erros de traduo. Se quiser colaborar com ama traduo em particular, entre em contato com um dos tradutores abaixo:. Gamarra Tradutor (es) em ordem alfabtica: Alvaro Netto alvaronetto@cetip.com.br Anderson Braulio andersonbraulio@gmail.com Daniel Vieira Costa danielvc@gmail.com Francisco gamarra francisco.gamarra@gmail.com Gamarra mauricio.gamarra@gmail.com Luiz Carlos Rodrigues luizcarlos_rodrigues@yahoo.com.br Marcel Castelo marcel.castelo@gmail.com Paulo Csar paulocol@gmail.com Pablo L. de Miranda pablolmiranda@gmail.com Renato Deggau rdeggau@gmail.com Rogrio Arajo rgildoaraujo@yahoo.com.br Wanderson Siqueira wandersonxs@gmail.com
Trabalhando com software orientado a objetos e banco de dados relacional, podemos ter alguns incmodos hoje em dia em ambientes empresariais. Hibernate uma ferramenta que mapeia o objeto/relacional para o ambiente Java. O termo de mapeamento de objeto/relacional (ou ORM # Object/Relational Mapping) se refere a tcnica de mapear uma representao de dados de um modelo de objeto para dados de modelo relacional com o esquema baseado em SQL O Hibernate no somente cuida do mapeamento de classes em Java para tabelas de banco de dados (e de tipos de dados em Java para tipos de dados em SQL), como tambm fornece facilidade de consultas e recuperao de dados, podendo tambm reduzir significantemente o tempo de desenvolvimento gasto com a manipulao manual de dados no SQL e JDBC. O objetivo do Hibernate de aliviar o desenvolvedor de 95 por cento das tarefas de programao relacionadas aos dados comuns de persistncia. O Hibernate talvez no seja a melhor soluo para aplicaes de dados-data-centric que somente usa stored-procedures para implementar a lgica de negcio no banco de dados, isto muito utilizado com o domnio de modelos orientado a objetos e lgicas de negcio em camadas do meio (middle-tier) baseadas em Java. Porm, o Hibernate poder certamente ajud-lo a remover ou encapsular o cdigo SQL de um vendedor especfico, ajudando tambm com a tarefa comum da traduo do resultado ajustado de uma representao para um grfico de objetos. Se voc for novo no Hibernate e no mapeamento Objeto/Relacional, ou at mesmo em Java, por favor, siga os Hibernate 3.2 cr2 viii
Prefcio
seguintes passos. 1. Leia Captulo 1, Introduo ao Hibernate para um tutorial com instrues passo-a-passo. O cdigo fonte para do tutorial est includo na distribuio no diretrio doc/reference/tutorial/. Leia o Captulo 2, Arquitetura para entender o ambiente onde o Hibernate pode ser utilizado. D uma olhada no diretrio de exemplo eg/ da distribuio do Hibernate, ele contm uma simples aplicao standalone. Copie seu driver JDBC para o diretrio lib/ e edite o arquivo etc/hibernate.properties, especificando corretamente os valores para seu banco de dados. Usando o prompt de comando no diretrio de distribuio, digite ant eg (usando Ant), ou no Windows, digite build eg. Use esta documentao de referencia como sua fonte primaria de informao. Considere ler tambm o livro Hibernate in Action (http://www.manning.com/bauer) caso voc precise de mais ajuda com o desenvolvimento de aplicaes ou caso prefira um tutorial passo-a-passo. Tambm visite o site http://caveatemptor.hibernate.org e faa o download da aplicao de exemplo do Hibernate em Ao. FAQs (perguntas feitas com mais freqncia) esto respondidas no site do Hibernate Demonstraes, exemplos e tutoriais esto disponveis no site do Hibernate. A rea da comunidade no site do Hibernate uma boa fonte de recursos para padres de projeto e vrias solues de integrao (Tomcat, JBoss AS, Struts, EJB, etc.).
2. 3.
4.
5. 6. 7.
Caso voc tenha dvidas, use o frum dos usurios encontrado no site do Hibernate. Ns tambm fornecemos um sistema para controle de bug#s (JIRA) para relatrios de erros e requisies de features. Se voc est interessado no desenvolvimento do Hibernate, junte-se a lista de e-mail dos desenvolvedores. Suporte comercial de desenvolvimento, suporte de produo e treinamento para o Hibernate est disponvel atravs do JBoss Inc. (veja http://www.hibernate.org/SupportTraining). O Hibernate um Projeto Profissional de Cdigo Aberto e um componente crtico da sute de produtos JBoss Enterprise Middleware System (JEMS).
ix
Esta a configurao mnima requerida das bibliotecas (observe que tambm foi copiado o hibernate3.jar da pasta principal do Hibernate) para o Hibernate na hora do desenvolvimento. O Hibernate permite que voc utilize mais ou menos bibliotecas. Veja o arquivo README.txt no diretrio lib/ da distribuio do Hibernate para maiores informaes sobre bibliotecas requeridas e opcionais. (Atualmente, a biblioteca Log4j no requerida, mas preferida por muitos desenvolvedores.) Agora, iremos criar uma classe que representa o evento que queremos armazenar na base de dados..
Introduo ao Hibernate
import java.util.Date; public class Event { private Long id; private String title; private Date date; public Event() {} public Long getId() { return id; } private void setId(Long id) { this.id = id; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } }
Voc pode ver que esta classe usa o padro JavaBean para o nomeamento convencional da propriedade getter e dos mtodos setter, como tambm a visibilidade private dos campos. Este um padro de projeto recomendado, mas no requerido. O Hibernate pode tambm acessar campos diretamente, o benefcio para os mtodos de acesso a robustez para o Refactoring. O construtor sem argumento requerido para instanciar um objeto desta classe com a reflexo. A propriedade id mantm um nico valor de identificao para um evento particular. Todas as classes persistentes da entidade (bem como aquelas classes dependentes de menos importncia) precisam de uma propriedade de identificao, caso ns queiramos usar o conjunto completo de caractersticas do Hibernate. De fato, a maioria das aplicaes (esp. aplicaes web) precisam destinguir os objetos pelo identificador, ento voc dever considerar esta, uma caracterstica em lugar de uma limitao. Porm, ns normalmente no manipulamos a identidade de um objeto, consequentemente o mtodo setter dever ser privado. O Hibernate somente nomear os identificadores quando um objeto for salvo. Voc pode ver como o Hibernate pode acessar mtodos pblicos, privados, e protegidos, como tambm campos (pblicos, privados, protegidos) diretamente. A escolha est at voc, e voc pode combinar isso para adaptar seu projeto de aplicao. O construtor sem argumentos um requerimento para todas as classes persistentes; O Hibernate tem que criar para voc os objetos usando Java Reflection. O construtor pode ser privado, porm, a visibilidade do pacote requerida para a procurao da gerao em tempo de execuo e recuperao eficiente dos dados sem a instrumentao de bytecode. Coloque este fonte Java no diretrio chamado src na pasta de desenvolvimento, e em seu pacote correto. O diretrio dever ser parecido como este:
. +lib <Hibernate and third-party libraries>
Introduo ao Hibernate
Veja que o Hibernate DTD muito sofisticado. Voc pode usar isso no mapeamento XML para auto-completar os elementos e atributos no seu editor ou IDE. Voc tambm pode abrir o arquivo DTD no seu editor a maneira mais fcil de ter uma viso geral de todos os elementos e atributos e dos padres, como tambm alguns comentrios. Note que o Hibernate no ir carregar o arquivo DTD da web, e sim do diretrio da aplicao (classpath). O arquivo DTD est includo no hibernate3.jar como tambm no diretrio src/ da distribuio do Hibernate. Ns omitiremos a declarao do DTD nos exemplos futuros para encurtar o cdigo. Isto, claro, no opcional. Entre os dois tags hibernate-mapping, inclua um elemento class. Todas as classes persistentes da entidade (novamente, poder haver mais tarde, dependncias sobre as classes que no so classes-primrias de entidades) necessitam do tal mapeamento, para uma tabela na base de dados SQL
<hibernate-mapping> <class name="events.Event" table="EVENTS"> </class> </hibernate-mapping>
Mais adiante iremos dizer ao Hibernate como fazer para persistir e carregar objetos da classe Event da tabela EVENTS, cada instancia representada por uma coluna na tabela. Agora, continuaremos com o mapeamento de uma nica propriedade identificadora para as chaves primrias da tabela. Alm disso, ns no iremos se importar com esta propriedade identificadora, ns iremos configurar uma estratgia de gerao de ids para uma chave primria de uma surrogate key:
<hibernate-mapping> <class name="events.Event" table="EVENTS"> <id name="id" column="EVENT_ID"> <generator class="native"/> </id> </class>
Introduo ao Hibernate
</hibernate-mapping>
O elemento id a declarao da propriedade identificadora, o name="id" declara o nome da propriedade Java o Hibernate ir usar os mtodos getter e setter para acessar a propriedade. O atributo da coluna informa ao Hibernate qual coluna da tabela EVENTS ns iremos usar como chave primria. O elemento generator especifica a estratgia de gerao do identificador, neste caso usaremos native, que escolhe a melhor estratgia dependendo da base de dados (dialeto) configurada. O Hibernate suporta a base de dados gerada, globalmente nica, bem como a atribuio aos identificadores da aplicao (ou toda estratgia escrita para uma extenso). Finalmente incluiremos as declaraes para as propriedades persistentes da classe no arquivo mapeado. Por default, nenhuma das propriedades da classe considerada persistente:
<hibernate-mapping> <class name="events.Event" table="EVENTS"> <id name="id" column="EVENT_ID"> <generator class="native"/> </id> <property name="date" type="timestamp" column="EVENT_DATE"/> <property name="title"/> </class> </hibernate-mapping>
Da mesma maneira que com o elemento id, o atributo name do elemento property informa ao Hibernate qual mtodo getter e setter dever usar. Assim, neste caso, o Hibernate ir procurar pelo getDate()/setDate(), como tambm pelo getTitle()/setTitle(). Porque fazer o mapeamento da propriedade date includo no atributo column, e no title no fazer? Sem o atributo column o Hibernate por padro usa o nome da propriedade como o nome da coluna. Isto trabalha muito bem para o title. Entretanto o date uma palavra-chave reservada na maioria dos bancos de dados, assim ns melhoramos o mapeamentos disto com um nome diferente. A prxima coisa interessante que mapemanto do title tambm falta o atributo type. O tipo que declaramos e o uso nos arquivos mapeados, no so como voc pode esperar, atributos de dados Java. Eles no so como os tipos de base de dados SQL. Esses tipos podem ser chamados de Tipos de mapeamento Hibernate, que so 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 converso correta e mapear o type prprio, caso o tipo do atributo no estiver presente no mapeamento. Em alguns casos, esta deteco automtica (que usa Reflection sobre as classes Java) poder no ter padro que voc espera ou necessita. Este o caso com a propriedade date. O Hibernate no pode saber se a propriedade (que do java.util.Date) pode mapear para uma coluna do tipo date do SQL, timestamp, ou time . Ns preservamos a informao cheia de datas e horas pelo mapeamento da propriedade com um conversor timestamp. Este arquivo de mapeamento deve ser salvo como Event.hbm.xml, corretamente no diretrio prximo ao arquivo fonte da Classe Java Event. A escolha do nome dos arquivos de mapeamento pode ser arbitrrio, porm o sufixo hbm.xml uma conveno da comunidade dos desenvolvedores do Hibernate. Esta estrutura do diretrio deve agora se parecer com isso:
. +lib <Hibernate and third-party libraries> +src +events Event.java Event.hbm.xml
Introduo ao Hibernate
Introduo ao Hibernate
</session-factory> </hibernate-configuration>
Note que esta configurao XML usa um diferente DTD. Ns configuraremos as SessionFactory do Hibernate uma factory global responsvel por uma base de dedados particular. Se voc tiver diversas bases de dados, use diversas configuraes <session-factory>, geralmente em diversos arquivos de configurao (para uma partida mais fcil). As primeiras quatro propriedades do elemento contm a configurao necessria para a conexo ao JDBC. A propriedade propriedade dialect do elemento especifica a variante particular do SQL que o Hibernate gera. O gerenciamento automtico de sesso do Hibernate para contextos de persistncia estar disponvel em breve. A opo hbm2ddl.auto habilita a gerao automtica de schemas da base de dados diretamente na base de dados. Isto tambm pode ser naturalmente desligado (removendo a opo de configurao) ou redirecionando para um arquivo com ajuda do SchemaExport nas tarefas do Ant. Finalmente, iremos adicionar os arquivos das classes de persistncia mapeadas na configurao. Copie este arquivo no diretrio fonte, assim isto ir terminar na raiz (root) do classpath. O Hibernate automaticamente procura por um arquivo chamado hibernate.cfg.xml na raiz do classpath, no startup.
Introduo ao Hibernate
</project>
Isto ir avisar ao Ant para adicionar todos os arquivos no diretrio lib terminando com .jar, para o classpath usado para compilao. Ir tambm copiar todos os arquivos no-java para o diretrio alvo (arquivos de configurao, mapeamento). Se voc rodar o ant agora, dever ter esta sada.
C:\hibernateTutorial\>ant Buildfile: build.xml copy-resources: [copy] Copying 2 files to C:\hibernateTutorial\bin compile: [javac] Compiling 1 source file to C:\hibernateTutorial\bin BUILD SUCCESSFUL Total time: 1 second
Esta classe no s produz a global SessionFactory no seu static initializer (chamado uma vez pela JVM quando a classe carregada), mas tambm esconde o fato de que isto usa um static singleton. Ela pode muito bem, enxergar a SessionFactory do JNDI em um application server. Se voc der SessionFactory um nome, no seu arquivo de configurao. O Hibernate ir, de fato, tentar uni-lo ao JNDI depois que estiver construdo. Para evitar este completamente este cdigo, voc tambm poderia usar Hibernate 3.2 cr2 7
Introduo ao Hibernate JMX deployment e deixar o continer JMX capaz, instanciar e unir um HibernateService no JNDI. Essas opes avanadas so discutidas no documento de referncia do Hibernate. Coloque o HibernateUtil.java no diretrio de arquivos de desenvolvimento(source), em um pacote aps o events:
. +lib <Hibernate and third-party libraries> +src +events Event.java Event.hbm.xml +util HibernateUtil.java hibernate.cfg.xml +data build.xml
Novamente, isto deve compilar sem problemas. Finalmente, ns precisamos configurar um sistema de logging o Hibernate usa commons logging e deixa voc escolher entre o Log4j e o logging do JDK 1.4 . A maioria dos desenvolvedores prefere o Log4j: copie log4j.properties da distribuio do Hibernate (est no diretrio etc/), para seu diretrio src, depois v em hibernate.cfg.xml. D uma olhada no exemplo de configurao e mude as configuraes se voc quizer ter uma sada mais detalhada. Por default, apenas as mensagems de startup e shwwn do Hibernate mostrada no stdout. O tutorial de infra-estrutura est completo - e ns j estamos preparados para algum trabalho de verdade com o Hibernate.
Introduo ao Hibernate
session.getTransaction().commit(); } }
Ns criamos um novo objeto Event, e passamos para o Hibernate. O Hibernate sabe como tomar conta do SQL e executa INSERTs no banco de dados. Vamos dar uma olhada na Session e no cdigo Transaction-handling antes de executarmos. Um Session uma unidade simples de trabalho. Por agora ns iremos pegar coisas simples e assumir uma granularidade de um-pra-um entre uma Session do Hibernate e uma transao de banco de dados. Para proteger nosso cdigo de um atual sistema subjacente de transao (nesse caso puro JDBC, mas tambm poderia rodar com JTA), nos usamos a API Transaction, que est disponvel na Session do Hibernate. O que a sessionFactory.getCurrentSession() faz? Primeiro, voc pode chamar quantas vezes e de onde quiser, uma vez voc recebe sua SessionFactory (fcil graas ao HibernateUtil). O mtodo getCurrentSession() sempre retorna a unidade de trabalho "corrente". Lembra de que ns mudamos a opo de configurao desse mecanismo para thread no hibernate.cfg.xml? Daqui em diante, o escopo da unidade de trabalho corrente a thread Java corrente que executa nossa aplicao. Entretanto, esta no toda a verdade. Uma Session comea quando primeiramente necessria, quando feita a primeira chamada getCurrentSession(). ento limitado pelo Hibernate para thread corrente. Quando a transao termina, tanto com commit quanto rollback, o Hibernate tambm desune a Session da thread e fecha isso pra voc. Se voc chamar getCurrentSession() novamente, voc receber uma nova Session e pode comear uma nova unidade de trabalho. Esse modelo de programao de limite de thread thread-bound, o modo mais popular de se usar o Hibernate. D uma olhada no Captulo 11, Transaes e Concorrncia para mais informaes a respeito de manipulao de transao e demarcao. Ns tambm pulamos qualquer manipulao de erro e rollback no exemplo anterior. Para executar esta primeira rotina, nos teremos que adicionar um ponto de chamada para o arquivo de build do Ant:
<target name="run" depends="compile"> <java fork="true" classname="events.EventManager" classpathref="libraries"> <classpath path="${targetdir}"/> <arg value="${action}"/> </java> </target>
O valor do argumento action, setado na linha de comando quando chamando esse ponto:
C:\hibernateTutorial\>ant run -Daction=store
Voc dever ver, aps a compilao, o startup do Hibernate e, dependendo da sua configurao, muito log de sada. No final voc ver a seguinte linha:
[java] Hibernate: insert into EVENTS (EVENT_DATE, title, EVENT_ID) values (?, ?, ?)
Este o INSERT executado pelo Hibernate, os pontos de interrogao representam parmetros de unio do JDBC. Para ver os valores substitudos, ou para diminuir a verbalidade do log, check seu llog4j.properties. Agora ns gostaramos de listar os eventos arquivados, ento ns adicionamos uma opo para o mtodo main:
if (args[0].equals("store")) { mgr.createAndStoreEvent("My Event", new Date()); }
Introduo ao Hibernate
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()); } }
O que ns fazemos aqui, usar uma query HQL (Hibernate Query Language), para carregar todos os objetos Event exitentes no banco de dados. O Hibernate ir gerar o SQL apropriado, enviar para o banco de dados e popular objetos Event com os dados. Voc pode criar queries mais complexas com HQL, claro. Agora, para executar e testar tudo isso, siga os passos a seguir: Execute ant run -Daction=store para armazenar algo no banco de dados e, claro, gerar o esquema do banco de dados antes pelo hbm2ddl. Agora desabilite hbm2ddl comentando a propriedade no seu arquivo hibernate.cfg.xml. Normalmente s se deixa habilitado em teste unitrios contnuos, mas outra carga de hbm2ddl pode remover tudo que voc j tenha arquivado. Sa configurao create, atualmente so traduzidas para "apague todas as tabelas do esquema, ento recrie todas quando a SessionFactory estiver pronta".
Se voc agora chamar o Ant com -Daction=list, voc dever ver os eventos que voc acabou de criar. Voc pode tambm chamar a ao store mais algumas vezes. Nota: A maioria dos novos usurios do Hibernate falha nesse ponto e ns regularmente, vemos questes sobre mensagens de erro de tabela no encontrada . Entretanto, se voc seguir os passos marcados acima, voc no ter esse problema, com o hbm2ddl criando o esquema do banco de dados na primeira execuo, e restarts subsequentes da aplicao iro usar este esquema. Se voc mudar o mapeamento e/ou o esquema do banco de dados, ter de re-habilitar o hbm2ddl mais uma vez.
10
Introduo ao Hibernate
public Person() {} // Accessor methods for all properties, private setter for 'id' }
Crie um novo arquivo de mapeamento, chamado Person.hbm.xml (no esquea a referencia ao DTD no topo)
<hibernate-mapping> <class name="events.Person" table="PERSON"> <id name="id" column="PERSON_ID"> <generator class="native"/> </id> <property name="age"/> <property name="firstname"/> <property name="lastname"/> </class> </hibernate-mapping>
Nos iremos agora criar uma associao entre estas duas entidades. Obviamente, pessoas (Person) podem participar de eventos, e eventos possuem participantes. As questes de design com que teremos de lidar so: direcionalidade, multiplicidade e comportamento de coleo.
11
Introduo ao Hibernate
Antes de mapearmos esta associao, pense no outro lado. Claramente, poderamos apenas fazer isto de forma unidirecional. Ou poderamos criar outra coleo no Event, se quisermos ser capaz de navegar bidirecionalmente, i.e. um - anEvent.getParticipants(). Isto no necessrio, de perspectiva funcional. Voc poderia sempre executar uma query explicita que retornasse os participantes de um evento em particular. Esta uma escolha de design que cabe a voc, mas o que claro nessa discusso a multiplicidade da associao: "muitos" valores em ambos os lados, ns chamamos isto uma associao muitos-para-muitos. Daqui pra frente, nos usaremos o mapeamento muitos-para-muitos do Hibernate:
<class name="events.Person" table="PERSON"> <id name="id" column="PERSON_ID"> <generator class="native"/> </id> <property name="age"/> <property name="firstname"/> <property name="lastname"/> <set name="events" table="PERSON_EVENT"> <key column="PERSON_ID"/> <many-to-many column="EVENT_ID" class="events.Event"/> </set> </class>
O Hibernate suporta todo tipo de mapeamento de coleo, sendo um <set> mais comum. Para uma associao muitos-para-muitos (ou relacionamento de entidade n:m ), uma tabela de associao necessria. Cada linha nessa tabela representa um link entre uma pessoa e um evento. O nome da tabela configurado com o atributo table do elemento set. O nome da coluna identificadora na associo, peloo lado da pessoa, definido com o elemento <key>, o nome da coluna pelo lado dos eventos, e definido com o atributo column do <many-to-many>. Voc tambm precisa dizer para o Hibernate a classe dos objetos na sua coleo (a classe do outro lado das colees de referncia). O esquema de mapeamento para o banco de dados est a seguir:
_____________ __________________ | | | | _____________ | EVENTS | | PERSON_EVENT | | | |_____________| |__________________| | PERSON | | | | | |_____________| | *EVENT_ID | <--> | *EVENT_ID | | | | EVENT_DATE | | *PERSON_ID | <--> | *PERSON_ID | | TITLE | |__________________| | AGE | |_____________| | FIRSTNAME | | LASTNAME | |_____________|
12
Introduo ao Hibernate
Aps carregar um Person e um Event, simplesmente modifique a coleo usando os mtodos normais de uma coleo. Como voc pode ver, no h chamada explcita para update() ou save(), o Hibernate detecta automaticamente que a coleo foi modificada e necessita ser atualizada. Isso chamado de checagem suja automtica, e voc tambm pode us-la modificando o nome ou a data de qualquer um dos seus objetos. Assim que eles estiverem no estado persistent, ou seja, limitado por uma Session do Hibernate em particular (i.e. eles foram carregados ou salvos dentro de uma unidade de trabalho), o Hibernate monitora qualquer alterao e executa o SQL em modo de escrita em segundo plano. O processo de sincronizao do estado da memria com o banco de dados, geralmente apenas no final de uma unidade de trabalho, chamado de flushing. No nosso cdigo, a unidade de trabalho termina com o commit da transao do banco de dados como definido pela opo de configurao da thread da classe CurrentSessionContext. Voc pode tambm querer carregar pessoas e eventos em diferentes unidades de trabalho. Ou voc modifica um objeto fora de uma Session, quando no se encontra no estado persistent (se j esteve neste estado anteriormente, chamamos esse estado de detached). Voc pode at mesmo modificar uma coleo quando esta se encontrar no estado detached.
private void addPersonToEvent(Long personId, Long eventId) { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); Person aPerson = (Person) session .createQuery("select p from Person p left join fetch p.events where p.id = :pid") .setParameter("pid", personId) .uniqueResult(); // Eager fetch the collection so we can use it detached Event anEvent = (Event) session.load(Event.class, eventId); session.getTransaction().commit(); // End of first unit of work aPerson.getEvents().add(anEvent); // aPerson (and its collection) is detached // Begin second unit of work Session session2 = HibernateUtil.getSessionFactory().getCurrentSession(); session2.beginTransaction(); session2.update(aPerson); // Reattachment of aPerson session2.getTransaction().commit(); }
A chamada update cria um objeto persistent novamente, voc poderia dizer que ele liga o objeto a uma nova unidade de trabalho, assim qualquer modificao que voc faa neste objeto enquanto estiver no estado detached pode ser salvo no banco de dados. Isso inclui qualquer modificao (adio/excluso) que voc faa em uma coleo da entidade deste objeto. Bom, isso no foi muito usado na nossa situao, porm, um importante conceito que voc pode aplicar em seus aplicativos. Agora, complete este exerccio adicionando uma nova ao ao mtodo main( ) da classe EventManager e chame-o pela linha de comando. Se voc precisar dos identificadores de uma pessoa ou evento o mtodo save() retorna estes identificadores (voc poder modificar alguns dos mtodos 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);
13
Introduo ao Hibernate
Este foi um exemplo de uma associao entre duas classes igualmente importantes, duas entidades. Como mencionado anteriormente, h outras classes e tipos dentro de um modelo tpico, geralmente "menos importantes". Alguns voc j viu, como um int ou uma String. Ns chamamos essas classes de value types, e suas instncias dependem de uma entidade particular. As instncias desses tipos no possuem sua prpria identidade, nem so compartilhados entre entidades (duas pessoas no referenciam o mesmo objeto firstname mesmo se elas tiverem o mesmo objeto firstname). Naturalmente, os value types no so apenas encontrados dentro da JDK (de fato, em um aplicativo Hibernate todas as classes JDK so consideradas como value types), mas voc pode tambm criar suas classes como, por exemplo, Address ou MonetaryAmount. Voc tambm pode criar uma coleo de value types. Isso conceitualmente muito diferente de uma coleo de referncias para outras entidades, mas em Java parece ser quase a mesma coisa.
A diferena comparada com o mapeamento anterior se encontra na parte element, que indica ao Hibernate que a coleo no contm referncias outra entidade, mas uma coleo de elementos do tipo String (a tag name em miniscula indica que se trata de um mapeamento do Hibernate para converso de tipos). Mais uma vez, o atributo table do elemento set determina o nome da tabela para a coleo. O elemento key define o nome da coluna de chave estrangeira na tabela de coleo. O atributo column dentro do elemento element define o nome da coluna onde os valores da String sero armazenados. D uma olhada no esquema atualizado:
_____________ __________________ | | | | _____________ | 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 | |_____________|
14
Introduo ao Hibernate
Voc pode observar que a chave primria da tabela da coleo de na verdade uma chave composta, usando ambas as colunas. Isso tambm implica que cada pessoa no pode ter endereos de e-mail duplicados, o que exatamente a semntica que precisamos para um set em Java. Voc pode agora tentar adicionar elementos a essa coleo, do mesmo modo que fizemos anteriormente ligando pessoas e eventos. o mesmo cdigo em Java:
private void addEmailToPerson(Long personId, String emailAddress) { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); Person aPerson = (Person) session.load(Person.class, personId); // The getEmailAddresses() might trigger a lazy load of the collection aPerson.getEmailAddresses().add(emailAddress); session.getTransaction().commit(); }
This time we didnt' use a fetch query to initialize the collection. Hence, the call to its getter method will trigger an additional select to initialize it, so we can add an element to it. Monitor the SQL log and try to optimize this with an eager fetch.
Como voc pode ver, esse uma mapeamento normal usando set em ambos documenentos de mapeamento. Observe que o nome das colunas em key e many-to-many esto trocados em ambos os documentos de mapeamento. A adio mais importante feita est no atributo inverse="true" no elemento set do mapeamento da coleo da classe Event. Isso significa que o Hibernate deve pegar o outro lado a classe Person quando necessitar encontrar informao sobre a relao entre as duas entidades. Isso ser muito mais facilmente compreendido quando voc analisar como a relao bidirecional entre as entidades criada.
15
Introduo ao Hibernate
Observe que os mtodos set e get da a coleo esto protegidos isso permite que classes e subclasses do mesmo pacote continuem acessando os mtodos, mas previne que qualquer outra classe, que no esteja no mesmo pacote, acesse a coleo diretamente. Voc provavelmente deve fazer a mesma coisa para a coleo do outro lado. E sobre o mapeamento do atributo inverse? Pra voc, e para o Java, um link bidirecional simplesmente o fato de ajustar corretamente as referncias de ambos os lados. O Hibernate, entretanto no possui informao necessria para corretamente adaptar os estados INSERT e UPDATE do SQL, e precisa de ajuda para manipular as propriedades das associaes bidirecionais. Fazer um lado da associao com o atributo inverse instrui o Hibernate para basicamente ignora-lo, considerando-o uma cpia do outro lado. Isso todo o necessrio para o Hibernate trabalhar com todas as possibilidades quando transformando um modelo de navegao bidirecional em esquema de banco de dados do SQL. As regras que voc possui para lembrar so diretas: Todas associaes bidirecionais necessitam que um lado possua o atributo inverse. Em uma associao de um-para-muitos, o lado de "muitos" deve conter o atributo inverse, j em uma associao de muitos-para-muitos voc pode pegar qualquer lado, no h diferena. Agora, vamos portar este exemplo para um pequeno aplicativo para internet.
16
Introduo ao Hibernate
O servlet manuseia somente requisies GET do HTTP, portanto o mtodo que iremos implementar doGet():
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { SimpleDateFormat dateFormatter = new SimpleDateFormat("dd.MM.yyyy"); try { // Begin unit of work HibernateUtil.getSessionFactory() .getCurrentSession().beginTransaction(); // Process request and render page... // End unit of work HibernateUtil.getSessionFactory() .getCurrentSession().getTransaction().commit(); } catch (Exception ex) { HibernateUtil.getSessionFactory() .getCurrentSession().getTransaction().rollback(); throw new ServletException(ex); } }
O pattern que estamos aplicando neste cdigo chamado session-per-request. Quando uma requisio chega ao servlet, uma nova Session do Hibernate aberta atravs da primeira chamada para getCurrentSession() em SessionFactory. Ento uma transao do banco de dados inicializada - todo acesso a dados deve ocorrer dentro de uma transao, no importando se o dado de leitura ou escrita. (ns no devemos usar o modo autocommit em aplicaes). Agora, as possibilidades de aes de uma requisio sero processadas e uma resposta HTML ser renderizada. Ns j iremos chegar nesta parte. Finalmente, a unidade de trabalho termina quando o processamento e a restituio so completados. Se ocorrer algum erro durante o processamento ou a restituio, uma exceo ser lanada e a transao do banco de dados encerrada. Isso completa o pattern session-per-request. Em vez de usar cdigo de demarcao de transao em todo servlet voc pode tambm criar um filtro servlet. D uma olhada no site do Hibernate e do Wiki para maiores informaes sobre esse pattern, chamado Open Session in View.
17
Introduo ao Hibernate
out.println("<html><head><title>Event Manager</title></head><body>"); // Handle actions if ( "store".equals(request.getParameter("action")) ) { String eventTitle = request.getParameter("eventTitle"); String eventDate = request.getParameter("eventDate"); 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); // Write HTML footer out.println("</body></html>"); out.flush(); out.close();
O estilo de cdigo acima, misturando linguagem HTML e Java no ser funcional em um aplicativo mais complexotenha em mente que neste manual ns estamos apenas ilustrando conceitos bsicos do Hibernate. O cdigo imprime um cabealho HTML e um rodap. Dentro desta pgina, mostrado um formulrio em HTML, para entrada de novos eventos, e uma lista de todos os eventos contidos no banco de dados. O primeiro mtodo trivial e apenas imprime uma pgina HTML:
private void printEventForm(PrintWriter out) { out.println("<h2>Add new event:</h2>"); out.println("<form>"); out.println("Title: <input name='eventTitle' length='50'/><br/>"); out.println("Date (e.g. 24.12.2009): <input name='eventDate' length='10'/><br/>"); out.println("<input type='submit' name='action' value='store'/>"); out.println("</form>"); }
O mtodo listEvents() usa a Session do Hibernate associada a thread atual para executar um query:
private void listEvents(PrintWriter out, SimpleDateFormat dateFormatter) { List result = HibernateUtil.getSessionFactory() .getCurrentSession().createCriteria(Event.class).list(); if (result.size() > 0) { out.println("<h2>Events in database:</h2>"); out.println("<table border='1'>"); out.println("<tr>"); out.println("<th>Event title</th>"); out.println("<th>Event date</th>"); out.println("</tr>"); for (Iterator it = result.iterator(); it.hasNext();) { Event event = (Event) it.next(); out.println("<tr>"); out.println("<td>" + event.getTitle() + "</td>"); out.println("<td>" + dateFormatter.format(event.getDate()) + "</td>"); out.println("</tr>"); } out.println("</table>"); } }
Finalmente, a action store passada pra o mtodo createAndStoreEvent(), que tambm usa a Session da th-
18
Pronto, o servlet est completo. Uma requisio para o servlet ser processada em uma Session e uma Transaction simples. Como anteriormente, no aplicativo standalone, o Hibernate pode automaticamente associar esses objetos a thread atual em execuo. Isso possibilita a liberdade de voc modelar seu cdigo e acessar o mtodo SessionFactory do jeito que achar melhor. Geralmente voc ir usar um design mais sofisticado e mover o cdigo de acesso a dados para dentro de objetos de acesso a dados (o patter DAO). Leia o Hibernate Wiki para maiores exemplos.
Esta target cria um arquivo chamado hibernate-tutorial.war no diretrio do seu projeto. Ele empacota todas as bibliotecas e o arquivo de descrio web.xml, o qual esperado no diretrio base do seu projeto:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xs <servlet> <servlet-name>Event Manager</servlet-name> <servlet-class>events.EventManagerServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>Event Manager</servlet-name> <url-pattern>/eventmanager</url-pattern> </servlet-mapping> </web-app>
Antes de voc compilar e fazer o deploy desta aplicao web, note que uma biblioteca adicional requerida: jsdk.jar. Esse o Java servlet development kit, se voc no possui esta biblioteca, faa seu download na pgina da Sun e copie-a para seu diretrio de bibliotecas. Entretanto, ser usado somente para a compilao e excludo do pacote WAR. Para compilar e instalar execute ant war no seu diretrio do projeto e copie o arquivo hibernate-tutorial.war para o diretrio webapp do Tomcat. Se voc no possui o Tomcat instalado faa o download e siga as instrues de instalao. Voc no precisa modificar nenhuma configurao do Tomcat para rodar este aplicatiHibernate 3.2 cr2 19
Introduo ao Hibernate vo. Uma rodando, acesse o aplicativo em Veja o log do Tomcat para observar a inicializao do Hibernate quando a primeira requisio chega ao servlet (o inicializador esttico dentro de HibernateUtil chamado) e para ter uma depurao detalhada se ocorrer alguma exceo.
http://localhost:8080/hibernate-tutorial/eventmanager.
vez
feito
deploy
com
Tomcat
1.5. Sumrio
Este manual cobriu os princpios bsicos para criao de uma aplicao simples do Hibernate e uma pequena aplicao web. Se voc j se sente seguro com o Hibernate, continue navegando na documentao de referncia por tpicos que voc acha interessante os tpicos mais questionados so: processo de transao (Captulo 11, Transaes e Concorrncia), uso da API (Captulo 10, Trabalhando com objetos) e caractersticas de consulta (Seo 10.4, Consultando). No esquea de visitar o site do Hibernate para obter mais tutoriais especializados.
20
Captulo 2. Arquitetura
2.1. Viso Geral
Uma viso bem ampla da arquitetura do Hibernate:
Esse diagrama mostra o Hibernate usando o banco de dados e a configurao de dados para prover persistncia de servios (e persistncia de objetos) para o aplicativo. Ns gostaramos de mostrar uma viso mais detalhada da arquitetura em execuo. Infelizmente, o Hibernate muito flexvel e suporta vrias aproximaes. Ns iremos mostrar os dois extremos. Na arquitetura mais simples o aplicativo fornece suas prprias conexes JDBC e gerencia suas transaes. Esta abordagem usa o mnimo de subconjuntos das APIs do Hibernate:
A arquitetura "completa" abstrai a aplicao de ter de lidar diretamente com JDBC/JTA e APIs e deixa o Hiber-
21
Arquitetura
Algumas definies dos objetos do diagrama: SessionFactory (org.hibernate.SessionFactory) Um cache threadsafe (imutveis) composto de identidades compiladas para um nico banco de dados. Uma fabrica para Session e um cliente de ConnectionProvider. Pode conter um cach opcional de dados (segundo nvel) reutilizveis entre transaes, no nvel de processo- ou cluster. Session (org.hibernate.Session) Objeto single-threaded, de vida curta, representando uma conversao entre o aplicativo e o armazenamento persistente. Cria uma camada sobre uma conexo JDBC. uma fabrica de Transaction. Possui um cach obrigatrio (primeiro nvel) de objetos persistentes, usado para navegao no grficos de objetos e pesquisa de objetos pelo identificador. Objetos persistentes e colees Objetos, de vida curta, single threaded contendo estado persistente e funo de negcios. Esses podem ser JavaBeans/POJOs, onde nica coisa especial sobre eles que so associados a (exatamente uma) Session. Quando a Session fechada, eles so separados e liberados para serem usados dentro de qualquer camada da aplicacao (Ex. diretamente como data transfer objects de e para a camada de apresentao) Objetos e colees desatachados e transientes Instncias de classes persistentes que ainda no esto associadas a uma Session. Eles podem ter sido instanciados pela aplicao e no persistido (ainda) ou eles foram instanciados por uma Session que foi encerrada. Transaction (org.hibernate.Transaction) (Opcional) Objeto de vida curta, single threaded, usado pela aplicao para especificar unidades atmicas de trabalho. Abstrai o aplicativo de lidar diretamente com transaes JDBC, JTA ou CORBA. Uma SessiHibernate 3.2 cr2 22
Arquitetura pode, em alguns casos, iniciar vrias Transactions. Entretanto, a demarcao da transao, mesmo utilizando API ou Transaction subjacentes, nunca opcional!
on
ConnectionProvider (org.hibernate.connection.ConnectionProvider) (Opcional) Uma fbrica de (e combinaes de) conexes JDBC. Abstrai a aplicao de lidar diretamente com Datasource ou DriverManager. No exposto para a aplicao, mas pode ser implementado ou estendido pelo programador. TransactionFactory (org.hibernate.TransactionFactory) (Opcional) Uma fbrica para instncias de Transaction. No exposta a aplicao, mas pode ser extendida/ implementada pelo programador. Extension Interfaces O Hibernate oferece vrias opes de interfaces estendidas que voc pode implementar para customizar sua camada persistente. Veja a documentao da API para maiores detalhes. Dada uma arquitetura simples, o aplicativo passa pelas APIs Transaction/TransactionFactory e/ou ConnectionProvider para se comunicar diretamente com a transao JTA ou JDBC.
Arquitetura
um escopo de transao JTA. Isso significa que voc no precisar mais abrir e fechar manualmente uma Session, isso se torna trabalho para um interceptor EJB do JBoss . Voc tambm no precisa se preocupar, nunca mais, com demarcao de transao em seu cdigo (a no ser que voc prefira escrever uma camada persistente portvel, para isso, use a API opcional do Hibernate Transaction). Voc deve chamar HibernateContext para acessar uma Session. HAR deployment:: Normalmente voc faz o deploy de um servio JMX do Hibernate usando um servio descritor de deploy do JBoss (em um EAR e/ou arquivo SAR), que suporta todas as configuraes usuais de uma SessionFactory do Hibernate. Entretanto, voc ainda precisa nomear todos os seus arquivos de mapeamento no descritor de deplorao. Se voc decidir usar o deploy opcional HAR, o JBoss ir automaticamente detectar todos os seus arquivos de mapeamento no seu arquivo HAR.
Consulte o manual do usurio do JBoss AS, para obter maiores informaes sobre essas opes. Another feature available as a JMX service are runtime Hibernate statistics. See Seo 3.4.6, Estatsticas do Hibernate. Outra opo disponvel como um servio JMX so as estatsticas de execuo do Hibernate. Veja a Seo 3.4.6, Estatsticas do Hibernate.
24
Arquitetura
- As sesses correntes so rastreadas e recebem um escopo por uma transao JTA. O processamento aqui exatamente igual ao antigo processo JTA. Consulte em Javadocs para maiores detalhes.
org.hibernate.context.JTASessionContext
- As sesses correntes so rastreadas por uma thread de execuo. Novamente, consulte em Javadocs para maiores detalhes.
org.hibernate.context.ThreadLocalSessionContext
- current sessions are tracked by thread of execution. However, you are responsible to bind and unbind a Session instance with static methods on this class, it does never open, flush, or close a Session.
org.hibernate.context.ManagedSessionContext
As duas primeiras implementaes usam o modelo de programao "uma sesso uma transao do banco de dados", tambm conhecida e usado como sesso por requisio. O comeo e o fim de uma sesso Hibernate so definidos pela durao da transao do banco de dados. Se voc usa demarcao programtica de transao (por exemplo. em J2SE puro ou com JTA /UserTransaction/BMT), voc recomendado a usar a API Hibernate Transaction para esconder a base do sistema de transao do seu cdigo. Se voc executa em um container EJB que suporta CMT, os limites das transaes so definidas declarativamente e voc no necessita de qualquer transao ou operao de demarcao de sesso no seu cdigo. Consulte Captulo 11, Transaes e Concorrncia para mais informaes exemplos de cdigo. O parmetro de configurao hibernate.current_session_context_class define que a implementao org.hibernate.context.CurrentSessionContext deve ser usada. Veka que para compatibilidade anterior, se este parmetro de configurao no determinado mas, um org.hibernate.transaction.TransactionManagerLookup configurado, Hibernate usar o org.hibernate.context.JTASessionContext. Tipicamente, o valor deste parmetro nomearia apenas a classe de implementao para usar; para as duas implementaes out-of-the-box, entretanto, h dois pequenos nomes correspondentes, "jta", "thread", and "managed".
25
Captulo 3. Configurao
Devido ao fato de o Hibernate ser projetado para operar em vrios ambientes diferentes, h um grande nmero de parmetros de configurao. Felizmente, a maioria tem valores default lgicos e o Hibernate distribudo com um arquivo hibernate.properties de exemplo no etc/ que mostra vrias opes. Apenas coloque o arquivo de exemplo no seu classpath e personalize-o.
Uma alternativa (s vezes melhor) especificar a classe mapeada, e permitir que o Hibernate encontre o documento de mapeamento para voc:
Configuration cfg = new Configuration() .addClass(org.hibernate.auction.Item.class) .addClass(org.hibernate.auction.Bid.class);
Ento
pelos arquivos de mapeamento chamados / e /org/hibernate/auction/Bid.hbm.xml no classpath. Esta abordagem elimina qualquer nome de arquivo de difcil compreenso.
org/hibernate/auction/Item.hbm.xml
Hibernate
procurar
Este no o nico caminho para passar as propriedades de configurao para o Hibernate. As vrias opes incluem: 1. 2. 3. 4. Passar uma instncia de java.util.Properties para Configuration.setProperties(). Colocar hibernate.properties no diretrio raiz do classpath. Determinar as propriedades do System usando java -Dproperty=value. Include <property> elements in hibernate.cfg.xml (discussed later). Incluir elementos <property> no hibernate.cfg.xml (discutido mais tarde). o caminho mais facil se voc quer comear mais rpido.
hibernate.properties
O Configuration entendido como um objeto startup-time, descartado uma vez que a SessionFactory criada.
26
Configurao
Hibernate permite sua aplicao instanciar mais do que uma SessionFactory. Isto til se voc est usando mais do que um banco de dados.
Assim que voc fizer algo que requer o acesso ao banco de dados, uma conexo JDBC ser obtida do pool. Para esse trabalho, ns necessitamos passar algumas propriedades da conexo JDBC para o Hibernate. Todos os nomes de propriedades Hibernate e semnticas so definidas org.hibernate.cfg.Environment. Ns iremos descrever agora o mais importantes configuraes de conexo JDBC. O Hibernate obter conexes( e pool) usando java.sql.DriverManager se voc determinar as seguintes propriedades: Tabela 3.1. Propriedades JDBC Hibernate Nome da Propriedade
hibernate.connection.driver_class hibernate.connection.url hibernate.connection.username hibernate.connection.password hibernate.connection.pool_size
Propsito Classe driver jdbc URL jdbc Usurio do banco de dados Senha do usurio do banco de dados Nmero mximo de connecxes no pool
O algoritmo de pool de conexes do prprio Hibernate entretanto completamente rudimentar. A inteno dele e ajudar a iniciar e no para usar em um sistema de produo ou at para testar desempenho. Voc deveria usar uma ferramente de pool de terceiros para conseguir melhor desempenho e estabilidade. Apenas especifique a propriedade hibernate.connection.pool_size com a definio do pool de conexes. Isto ir desligar o pool interno do Hibernate. Por exemplo, voc pode gostar de usar C3P0. O C3P0 um pool conexo JDBC de cdigo aberto distribudo junto com Hibernate no diretrio lib. O Hibernate usar o C3P0ConnectionProvider para o pool de conexo se voc configurar a propriedade hibernate.c3p0.*. Se voc gostar de usar Proxool consulte ao pacote hibernate.properties e o web site do Hibernate para mais informaes. Aqui um exemplo de arquivo hibernate.properties para C3P0:
27
Configurao
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 aplicao, voc deve configurar o Hibernate para obter conexes de um application server Datasource registrado no JNDI. Voc necessitar determinar pelo menos uma das seguintes propriedades: Tabela 3.2. Propriedades do Datasource do Hibernate Nome da Propriedade
hibernate.connection.datasource hibernate.jndi.url hibernate.jndi.class hibernate.connection.username hibernate.connection.password
Propsito Nome datasource JNDI URL do fornecedor JNDI (opcional) Classe do JNDI InitialContextFactory (opcional) Usurio do banco de dados (opcional) Senha do usurio do banco de dados (opcional)
Eis um exemplo de arquivo hibernate.properties para um servidor de aplicao fornecedor de datasources JNDI:
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
Conexes JDBC obtidas de um datasource JNDI iro automaticamente iro participar das transaes gerenciadas pelo container no servidor de aplicao. Arbitrariamente as propriedades de conexo podem ser acrescentandas ao "hibernate.connnection" ao nome da propriedade. Por exemplo, voc deve especificar o charSet usando hibernate.connection.charSet.t. Voc pode definir sua prpria estratgia de plugin para obter conexes JDBC implementando a interface org.hibernate.connection.ConnectionProvider. Voc pode escolher uma implementao customizada setando hibernate.connection.provider_class.
Configurao
Propsito O nome da classe de um Dialeto que permite o Hibernate gerar SQL otimizado para um banco de dados relacional em particular. Ex. full.classname.of.Dialect
hibernate.show_sql
Escreve todas as instrues SQL no console. Esta uma alternativa a configurar a categoria de log org.hibernate.SQL para debug. Ex. true | false
hibernate.format_sql
hibernate.default_schema
Qualifica no sql gerado, os nome das tabelas sem qualificar com schena/tablespace dado Ex. SCHEMA_NAME
hibernate.default_catalog
Qualifica no sql gerado, os nome das tabelas sem qualificar com catlogo dado Ex. CATALOG_NAME
hibernate.session_factory_name
O SessionFactory ir automaticamente se ligar a este nome no JNDI depois de ter sido criado. Ex. jndi/composite/name
hibernate.max_fetch_depth
Estabelece a "profundidade" mxima para rvore outer join fetch para associaes finais nicas(one-to-one,many-to-one). Um 0 desativa por default a busca outer join. eg. Valores recomendados entre0 e 3
hibernate.default_batch_fetch_size
Determina um tamanho default para busca de associaes em lotes do Hibernate eg. Valores recomendados 4, 8, 16
hibernate.default_entity_mode
Determina um modo default para representao de entidades para todas as sesses abertas desta SessionFactory dynamic-map, dom4j, pojo
hibernate.order_updates
Fora o Hibernate a ordenar os updates SQL pelo valor da chave primria dos itens a serem atualizados. Isto resultar em menos deadlocks nas transaes em sistemas altamente concorrente.
29
If enabled, Hibernate will collect statistics useful for performance tuning. Se habilitado, o Hibernate coletar estatsticas ties para performance tuning dos bancos. Ex. true | false
hibernate.use_identifer_rollback
Se habilitado, propriedades identificadoras geradas sero zeradas para os valores default quando os objetos forem apagados. Ex. true | false
hibernate.use_sql_comments
Se ligado, o Hibernate ir gerar comentrios dentro do SQL, para facilitar o debugging, o valor default false. eg. true | false
Propsito Um valor maior que zero determina o tamanho do fetch do JDBC( chamadas Statement.setFetchSize()). Um valor maior que zero habilita uso de batch updates JDBC2 pelo Hibernate. Ex. valores recomentados entre 5 e 30
hibernate.jdbc.batch_size
hibernate.jdbc.batch_versioned_data
Sete esta propriedade como true se seu driver JDBC retorna o nmero correto de linhas no executeBatch() ( usualmente seguro tornar esta opo ligada). O Hibernate ento ir usar betched DML para automaticamente versionar dados. false por default. Ex. true | false
hibernate.jdbc.factory_class
Escolher um Batcher customizado. Muitas aplicaes no iro necessitar desta propriedade de configurao Ex. classname.of.Batcher
hibernate.jdbc.use_scrollable_resultset
Habilita o uso de JDBC2 scrollable resultsets pelo Hibernate. Essa propriedade somente necessaria quando se usa Conexees JDBC providas pelo usurio, caso contrrio o Hibernate os os metadados da conexo. Ex. true | false
30
Propsito Use streams para escrever/ler tipos binary ou serializable para/a o JDBC( propriedade a nvel de sistema). Ex. true | false
hibernate.jdbc.use_get_generated_keys
Possibilita
uso PreparedStatement.getGeneratedKeys() do JDBC3 para recuperar chaves geradas nativamente depois da inserp. Requer driver JDBC3+ e JRE1.4+, determine para false se seu driver tem problemas com gerador de indentificadores Hibernate. Por default, tente determinar o driver capaz de usar metadados da conexo. Ex. true|false
hibernate.connection.provider_class
O nome da classe de um ConnectionProvider personalizado o qual prover conexes JDBC para o Hibernate. Ex. classname.of.ConnectionProvider
hibernate.connection.isolation
Determina o nvel de isolamento de uma transao JDBC. Verifique java.sql.Connection para valores siginificativos mas note que a maior parte dos bancos de dados no suportam todos os nveis de isolamento. Ex. 1, 2, 4, 8
hibernate.connection.autocommit
Habilita autocommit para conexes no pool JDBC( no recomendado). Ex. true | false
hibernate.connection.release_mode
Especifica quando o Hibernate deve liberar conexes JDBC. Por default, uma conexo JDBC retida at a sesso est explicitamente fechada ou desconectada. Para um datasource JTA do servidor de aplicao, voc deve usar after_statement para forar s liberao da conexes depois de todas as chamadas JDBC. Para uma conexo no-JTA, freqentemente faz sentido liberar a conexo ao fim de cada transao, usando after_transaction. auto escolheremos after_statement para as estratgias de transaoes JTA e CMT e after_transaction para as estratgias de transao JDBC Ex. auto (default) | on_close | after_transaction |
after_statement
Note that this setting only affects Sessions returned from SessionFactory.openSession. For Sessions obtained through SessionFactory.getCurrentSession, the CurrentSessionContext implementation configured for use controls the Hibernate 3.2 cr2 31
Configurao Nome da Propriedade Propsito connection release mode for those Sessions. See Seo 2.5, Sesses contextuais
hibernate.connection.<propertyName>
Passa a propriedade JDBC propertyName para DriverManager.getConnection(). Passar a propriedade propertyName para o InitialContextFactory JNDI.
hibernate.jndi.<propertyName>
hibernate.cache.use_minimal_puts
Otimizar operao de cach de segundo nvel para minimizar escritas, ao custo de leituras mais frequantes. Esta configurao mais til para cachs clusterizados e, no Hibernate3, habilitado por default para implementaes de cach clusterizar. Ex. true|false
hibernate.cache.use_query_cache
Habilita a cache de consultas, Mesmo assim, consultas individuais ainda tem que ser habilitadas para o cache. Ex. true|false
hibernate.cache.use_second_level_cache
May be used to completely disable the second level cache, which is enabled by default for classes which specify a <cache> mapping. Pode ser usada para desabilitar completamente ocache de segundo nvel, o qual est habilitado por default para classes que especificam um mapeamento <cache>. Ex. true|false
hibernate.cache.query_cache_factory
O nome de uma classe que implementa a interface QueryCache personalizada, por default, um StandardQueryCache criado automaticamente. Ex. classname.of.QueryCache
hibernate.cache.region_prefix
Um prefixo para usar nos nomes da rea especial do cach de segundo nvel. Ex. prefix
hibernate.cache.use_structured_entries
Forces Hibernate to store data in the second-level cache in a more human-friendly format. Fora o Hibernate armazenar dados no cach se segundo nvel em 32
Propsito O nome da clase de um a TransactionFactory para usar com API Transaction ( por default JDBCTransactionFactory). Ex. classname.of.TransactionFactory
jta.UserTransaction
Um nome JNDI usado pelo JTATransactionFactory para obter uma UserTransaction JTA a partir do servidor de aplicao. Ex. jndi/composite/name
hibernate.transaction.manager_lookup_class
O nome da classe de um TransactionManagerLookup requerido quando caching a nvel JVM esta habilitado ou quando estivermos usando um generator hilo em um ambiente JTA. Ex. classname.of.TransactionManagerLookup
hibernate.transaction.flush_before_completion
Se habilitado, a sesso ser automaticamente limpa antes da fase de concluso da transao. preferivel a gerncia interna e automtica do contexto da sesso, veja Seo 2.5, Sesses contextuais Ex. true | false
hibernate.transaction.auto_close_session
Se habilitado, a sesso ser automaticamente fechada aps a fase de concluso da transao. preferivel a gerncia interna e automtica do contexto da sesso, veja Seo 2.5, Sesses contextuais Ex. true | false
Propsito Fornee uma estratgia (personalizada) para extenso da Session "corrente". Veja Seo 2.5, Sesses contextuais para mais informao sobre estratgias internas. Ex. jta | thread | managed | custom.Class
hibernate.query.factory_class
or
org.hibernate.hql.classic.ClassicQueryTransla
Mapeamento a partir de smbolos em consultas HQL para smbolos SQL( smbolos devem ser funes ou nome literais , por exemplo). eg.
hqlLiteral=SQL_LITERAL, hqlFuncti-
on=SQLFUNC hibernate.hbm2ddl.auto
Automaticamente valida ou exporta schema DDL para o banco de dados quando o SessionFactory criads. Com create-drop, o schema do banco de dados ser excluido quando a create-drop for fechada esplicitamente. Ex. validate | update | create | create-drop
hibernate.cglib.use_reflection_optimizer
Habilita o uso de CGLIB em vez de reflexo em tempo de execuo ( propriedade a nvel de sistema). Reflexo pode algumas vezes ser til quando controlar erros, note que o Hibernate sempre ir requerer a CGLIB mesmo se voc desligar o otimizador. Voc no pode determinar esta propriedade no hibernate.cfg.xml. Ex. true | false
deve
sempre
34
Configurao RDBMS MySQL with MyISAM Oracle (any version) Oracle 9i/10g Sybase Sybase Anywhere Microsoft SQL Server SAP DB Informix HypersonicSQL Ingres Progress Mckoi SQL Interbase Pointbase FrontBase Firebird Dialect
org.hibernate.dialect.MySQLMyISAMDialect org.hibernate.dialect.OracleDialect org.hibernate.dialect.Oracle9Dialect org.hibernate.dialect.SybaseDialect org.hibernate.dialect.SybaseAnywhereDialect org.hibernate.dialect.SQLServerDialect org.hibernate.dialect.SAPDBDialect org.hibernate.dialect.InformixDialect org.hibernate.dialect.HSQLDialect org.hibernate.dialect.IngresDialect org.hibernate.dialect.ProgressDialect org.hibernate.dialect.MckoiDialect org.hibernate.dialect.InterbaseDialect org.hibernate.dialect.PointbaseDialect org.hibernate.dialect.FrontbaseDialect org.hibernate.dialect.FirebirdDialect
35
Configurao
As propriedades prefixadas pelo hibernate.cache permite voc usar um sistema de cach de segundo nvel em um processo executado em clustercom Hibernate. Veja Seo 19.2, The Second Level Cache para mais detalhes.
Faria com que os smbolos true e false passasem a ser traduzidos para literais inteiro no SQL gerado.
hibernate.query.substitutions toLowercase=LOWER
3.5. Logging
Hibernate registra vrios eventos usando Apache commons-logging. O servio commons-logging direcionar a sada para o Apache Log4j ( se voc incluir log4j.jarr no seu classpath) ou JDK1.4 logging( se estiver em uso JDK1.4 ou maior). Voc pode fazer o download do Log4j a partir de http://jakarta.apache.org. Para usar Log4j voc necessitar colocar um arquivo log4j.properties no seu classpath, um exemplo de arquivo de propriedades distribudo com o Hibernate no diretrio src/. We strongly recommend that you familiarize yourself with Hibernate's log messages. A lot of work has been put into making the Hibernate log as detailed as possible, without making it unreadable. It is an essential troubleshooting device. The most interesting log categories are the following: Ns recomendamos enfaticamente que voc se familiarize-se com mensagens de log do Hibernate. Uma parte do trabalho tem sido posto em fazer o log Hibernate to detalhado quanto possvel, sem faz-lo ilegvel. um essencial dispositivos de controle de erros. As categorias de log mais interessantes so as seguintes: Tabela 3.9. Categorias de Log do Hibernate Categoria
org.hibernate.SQL org.hibernate.type org.hibernate.tool.hbm2dd l
Funo Registra todas as instrues SQL DML a medida que elas so executadas Registra todos os parmetros JDBC Registra todas as instrues SQL DDL a medida que elas so executadas
36
Configurao Categoria
org.hibernate.pretty
Funo Log the state of all entities (max 20 entities) associated with the session at flush time Registra o estado de todas as entidades (mximo 20 entidades) associadas a session no momento da limpeza (flush). Registra todas as atividades de cach de segundo nvel Registra atividades relacionada a transao Registra todas as requisies de recursos JDBC Registra instrues SQL e HQL durante a anlise da consultas Registra todas as requisies de autorizao JAAS Registra tudo ( uma parte das informaes, mas muito til para controle de erros )
Quando desenvolver aplicaes com Hibernate, voc deve quase sempre trabalhar com debug debug para a categoria org.hibernate.SQL, ou, alternativamente, a com a propriedade hibernate.show_sql habilitada.
org.hibernate.cfg.ImprovedNamingStrategy
37
Configurao
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <!-- a SessionFactory instance listed as /jndi/name --> <session-factory name="java:hibernate/SessionFactory"> <!-- properties --> <property name="connection.datasource">java:/comp/env/jdbc/MyDB</property> <property name="dialect">org.hibernate.dialect.MySQLDialect</property> <property name="show_sql">false</property> <property name="transaction.factory_class"> org.hibernate.transaction.JTATransactionFactory </property> <property name="jta.UserTransaction">java:comp/UserTransaction</property> <!-- mapping files --> <mapping resource="org/hibernate/auction/Item.hbm.xml"/> <mapping resource="org/hibernate/auction/Bid.hbm.xml"/> <!-- cache settings --> <class-cache class="org.hibernate.auction.Item" usage="read-write"/> <class-cache class="org.hibernate.auction.Bid" usage="read-only"/> <collection-cache collection="org.hibernate.auction.Item.bids" usage="read-write"/> </session-factory> </hibernate-configuration>
Como voc pode ver, a vantagem deste enfoque a externalizao dos nomes dos arquivos de mapeamento para configurao. O hibernate.cfg.xml tambm 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 so equivalente, exceo dos benefcios acima mencionados de usar a sintaxe de XML. Com a configurao do XML, iniciar o Hibernate ento to simples como
SessionFactory sf = new Configuration().configure().buildSessionFactory();
Configurao
iniciado. Ligao (binding) Session na JTA: A Session do Hibernate pode automaticamente ser ligada ao escopo da transaes JTA. Simplesmente localizando a SessionFactory da JNDI e obtendo aSession corrente. Deixe o Hibernate cuidar da limpeza e encerramento da Session quando as transaes JTA terminarem. A Demarcao de transao pode ser declarativa (CMT) ou programtica(BMT/Transao do usurio). JMX deployment: Se voc usa um JMX servidor de aplicaes capaz (ex. Jboss AS), voc pode fazer a instao do Hibernate como um Mbean controlado. Isto evita ter que iniciar uma linha de cdigo para construir sua SessionFactory de uma Configuration. O container iniciar seu HibernateService, e idealmente tambm cuidar das dependncias de servios (DataSources, tm que estar disponveis antes do Hibernate iniciar, etc.).
Dependendo em seu ambiente, voc poderia ter que ajustar a opo de configurao hibernate.connection.aggressive_release para verdadeiro ( true ), se seu servidor de aplicaes lanar exees "reteno de coneco".
delegada a transao a um container gerenciador se a transao existente estiver de acordo neste contexto (ex: mtodo bean sesso EJB), se no uma nova transao iniciada e uma transao controlado por um bean usada.
org.hibernate.transaction.CMTTransactionFactory
delega para um container gerenciador de transaes JTA Voc tambm pode definir suas prprias estratgias de transao ( para um servio de transao CORBA por exemplo). Algumas caractersticas no Hibernate (ex., o cache de segundo nvel, sesses contextuais com JTA, etc.) requerem acesso a JTA TransactionManager em um ambiente controlado. Em um servidor de aplicao voc tem que especificar como o Hibernate pode obter uma referncia para a TransactionManager, pois o J2EE no padronize um mecanismo simples : Tabela 3.10. Gerenciadores de transaes JTA
39
Application Server JBoss Weblogic WebSphere WebSphere 6 Orion Resin JOTM JOnAS JRun4 Borland ES
Configurao the transaction completes, and aggressively release JDBC connections after each statement. This allows the Sessions to be managed by the lifecycle of the JTA transaction to which it is associated, keeping user code clean of such management concerns. Your code can either use JTA programmatically through UserTransaction, or (recommended for portable code) use the Hibernate Transaction API to set transaction boundaries. If you run in an EJB container, declarative transaction demarcation with CMT is preferred.
This file is deployed in a directory called META-INF and packaged in a JAR file with the extension .sar (service archive). You also need to package Hibernate, its required third-party libraries, your compiled persistent classes, as well as your mapping files in the same archive. Your enterprise beans (usually session beans) may be kept in their own JAR file, but you may include this EJB JAR file in the main service archive to get a single
41
Configurao (hot-)deployable unit. Consult the JBoss AS documentation for more information about JMX service and EJB deployment.
42
private Cat mother; private Set kittens = new HashSet(); private void setId(Long id) { this.id=id; } public Long getId() { return id; } void setBirthdate(Date date) { birthdate = date; } public Date getBirthdate() { return birthdate; } void setWeight(float weight) { this.weight = weight; } public float getWeight() { return weight; } public Color getColor() { return color; } void setColor(Color color) { this.color = color; } void setSex(char sex) { this.sex=sex; } public char getSex() { return sex;
43
Persistent Classes
} void setLitterId(int id) { this.litterId = id; } public int getLitterId() { return litterId; } void setMother(Cat mother) { this.mother = mother; } public Cat getMother() { return mother; } void setKittens(Set kittens) { this.kittens = kittens; } public Set getKittens() { return kittens; } // addKitten not needed by Hibernate public void addKitten(Cat kitten) { kitten.setMother(this); kitten.setLitterId( kittens.size() ); kittens.add(kitten); } }
The identifier property is strictly optional. You can leave them off and let Hibernate keep track of object identifiers internally. We do not recommend this, however. In fact, some functionality is available only to classes which declare an identifier property: Transitive reattachment for detached objects (cascade update or cascade merge) - see Seo 10.11, Transitive persistence
Session.saveOrUpdate() Session.merge()
We recommend you declare consistently-named identifier properties on persistent classes. We further recommend that you use a nullable (ie. non-primitive) type.
Persistent Classes
A central feature of Hibernate, proxies, 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, but you won't be able to use proxies for lazy association fetching - which will limit your options for performance tuning. You should also avoid declaring public final methods on the non-final classes. If you want to use a class with a public final method, you must explicitly disable proying by setting lazy="false".
Properties need not be declared public - Hibernate can persist a property with a default, protected or private get / set pair.
Hibernate guarantees equivalence of persistent identity (database row) and Java identity only inside a particular session scope. So as soon as we mix instances retrieved in different sessions, we must implement equals() and hashCode() if we wish to have meaningful semantics for Sets. The most obvious way is to implement equals()/hashCode() by comparing the identifier value of both objects. If the value is the same, both must be the same database row, they are therefore equal (if both are added to a Set, we will only have one element in the Set). Unfortunately, we can't use that approach with generated identifiers! Hibernate will only assign identifier values to objects that are persistent, a newly created instance will
45
Persistent Classes not have any identifier value! Furthermore, if an instance is unsaved and currently in a Set, saving it will assign an identifier value to the object. If equals() and hashCode() are based on the identifier value, the hash code would change, breaking the contract of the Set. See the Hibernate website for a full discussion of this problem. Note that this is not a Hibernate issue, but normal Java semantics of object identity and equality. We recommend implementing equals() and hashCode() using Business key equality. Business key equality means that the equals() method compares only the properties that form the business key, a key that would identify our instance in the real world (a natural candidate key):
public class Cat { ... public boolean equals(Object other) { if (this == other) return true; if ( !(other instanceof Cat) ) return false; final Cat cat = (Cat) other; if ( !cat.getLitterId().equals( getLitterId() ) ) return false; if ( !cat.getMother().equals( getMother() ) ) return false; return true; } public int hashCode() { int result; result = getMother().hashCode(); result = 29 * result + getLitterId(); return result; } }
Note that a business key does not have to be as solid as a database primary key candidate (see Seo 11.1.3, Considerando a identidade do objeto). Immutable or unique properties are usually good candidates for a business key.
46
Persistent Classes
<property name="name" column="NAME" type="string"/> <property name="address" column="ADDRESS" 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 that even though associations are declared using target class names, the target type of an associations may also be a dynamic entity instead of a POJO. After setting the default entity mode to dynamic-map for the SessionFactory, we can at runtime work with Maps of Maps:
Session s = openSession(); Transaction tx = s.beginTransaction(); Session s = openSession(); // 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();
The advantages of a dynamic mapping are quick turnaround time for prototyping without the need for entity class implementation. However, you lose compile-time type checking and will very likely deal with many exceptions at runtime. Thanks to the Hibernate mapping, the database schema can easily be normalized and sound, allowing to add a proper domain model implementation on top later on. Entity representation modes can also be set on a per Session basis:
Session dynamicSession = pojoSession.getSession(EntityMode.MAP); // Create a customer Map david = new HashMap(); david.put("name", "David"); dynamicSession.save("Customer", david);
47
Persistent Classes
Please note that the call to getSession() using an EntityMode is on the Session API, not the SessionFactory. That way, the new Session shares the underlying JDBC connection, transaction, and other context information. This means you don't have tocall flush() and close() on the secondary Session, and also leave the transaction and connection handling to the primary unit of work. More information about the XML representation capabilities can be found in Captulo 18, Mapeamento XML.
4.5. Tuplizers
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 which knows how to create such a data structure and how to extract values from and inject values into such a data structure. For example, for the POJO entity mode, the correpsonding tuplizer knows how create the POJO through its constructor and how to access the POJO properties using the defined property accessors. There are two high-level types of Tuplizers, represented by the org.hibernate.tuple.EntityTuplizer and org.hibernate.tuple.ComponentTuplizer interfaces. EntityTuplizers are responsible for managing the above mentioned contracts in regards to entities, while ComponentTuplizers do the same for components. Users may also plug in their own tuplizers. Perhaps you require that a 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. Tuplizers definitions are attached to the entity or component mapping they are meant to manage. Going back to the example of our customer entity:
<hibernate-mapping> <class entity-name="Customer"> <!-Override the dynamic-map entity-mode tuplizer for the customer entity --> <tuplizer entity-mode="dynamic-map" class="CustomMapTuplizerImpl"/> <id name="id" type="long" column="ID"> <generator class="sequence"/> </id> <!-- other properties --> ... </class> </hibernate-mapping>
public class CustomMapTuplizerImpl extends org.hibernate.tuple.DynamicMapEntityTuplizer { // override the buildInstantiator() method to plug in our custom map... protected final Instantiator buildInstantiator( org.hibernate.mapping.PersistentClass mappingInfo) { return new CustomMapInstantiator( mappingInfo ); } private static final class CustomMapInstantiator extends org.hibernate.tuple.DynamicMapInstantitor {
48
Persistent Classes
// override the generateMap() method to return our custom map... protected final Map generateMap() { return new CustomMap(); } } }
49
50
<property name="name" type="string"/> </subclass> </class> <class name="Dog"> <!-- mapping for Dog could go here --> </class> </hibernate-mapping>
Discutir agora o contedo deste documento de mapeamento. Iremos apenas descrever os elementos do documento e atributos que so utilizados pelo Hibernate em tempo de execuo. O documento de mapeamento tambm contm alguns atributos adicionais e opcionais alm de elementos que afetam os esquemas de banco de dados exportados pela ferramenta de exportao de esquemas. (Por exemplo, o atributo not-null).
5.1.1. Doctype
Todos os mapeamentos de XML devem declarar o doctype exibido. O DTD atual pode ser encontrado na URL abaixo, no diretrio 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 conexo de internet, compare a declarao do seu DTD com o contedo do seu classpath 5.1.1.1. EntityResolver As mentioned previously, Hibernate will first attempt to resolve DTDs in its classpath. The manner in which it does this is by registering a custom org.xml.sax.EntityResolver implementation with the SAXReader it uses to read in the xml files. This custom EntityResolver recognizes two different systemId namespaces. a hibernate namespace is recognized whenever the resolver encounteres a systemId starting with http://hibernate.sourceforge.net/; the resolver attempts to resolve these entities via the classlaoder which loaded the Hibernate classes. a user namespace is recognized whenever the resolver encounteres a systemId using a classpath:// URL protocol; the resolver will attempt to resolve these entities via (1) the current thread context classloader and (2) the classloader which loaded the Hibernate classes.
51
Where types.xml is a resource in the your.domain package and contains a custom Seo 5.2.3, Tipos de valores personalizados.
5.1.2. hibernate-mapping
Este elemento tem diversos atributos opcionais. Os atributos schema e catalog especificam que tabelas referenciadas neste mapeamento pertencem ao esquema e/ou ao catalogo nomeado. Se especificados, os nomes das tabelas iro ser qualificados no schema ou catalog dado. Se no, os nomes das tabelas no sero qualificados. O atributo default-cascade especifica qual estilo de cascata ser assumido pelas propriedades e colees que no especificarm um atributo cascade. O atributo auto-import nos deixa utilizar nomes de classes no qualificados na linguagem de consulta, por default.
<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" />
(5)
(6)
(7)
(opcional): O nome do esquema do banco de dados. catalog (opcional): O nome do catlogo do banco de dados. default-cascade (opcional default nenhum ): Um estilo cascata default. default-access (opcional default property): A estratgia que o Hibernate deve utilizar para acessar todas as propridades. Pode ser uma implementao prpria de PropertyAccessor. default-lazy (opcional - default true): O valor default para atributos lazy da classe e dos mapeamentos de colees. auto-import ((opcional - default true): Especifica se ns podemos usar nomes de classess no qualificados (das classes deste mapeamento) na linguagem de consulta. package (opcional): Especifica um prefixo da package para assumir para nomes de classes no qualificadas no documento de mapeamento.
schema
Se voce tem duas classes persistentes com o mesmo nome (no qualificadas), voc deve setar auto-import="false". O Hibernate ir gerar uma exceo se voc tentar setar duas classes para o mesmo nome "importado". Observe que o elemento hibernate-mapping permite a voc aninhar diversos mapeamentos de <class> persistentes, como mostrado abaixo. Entretanto, uma boa prtica (e esperado por algumas ferramentas)o mapeamento de apenas uma classe persistente simples (ou uma hierarquia de classes simples) em um arquivo de mapeamento e nomea-la aps a superclasse persistente, por exemplo: Cat.hbm.xml, Dog.hbm.xml, ou se estiver usando herana, Animal.hbm.xml.
5.1.3. class
Voc pode declarar uma classe persistente utilizando o elemento class:
<class name="ClassName" table="tableName" discriminator-value="discriminator_value" mutable="true|false" schema="owner" catalog="catalog" proxy="ProxyInterface" (1) (2) (3) (4) (5) (6) (7)
52
dynamic-update="true|false" dynamic-insert="true|false" select-before-update="true|false" polymorphism="implicit|explicit" where="arbitrary sql where condition" persister="PersisterClass" batch-size="N" optimistic-lock="none|version|dirty|all" lazy="true|false" entity-name="EntityName" check="arbitrary sql check condition" rowid="rowid" subselect="SQL expression" abstract="true|false" node="element-name" />
(8) (9) (10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21)
(1)
(2)
(3)
(7)
(8)
(9)
(10)
(11)
(12)
(13) (14)
(18)
(19)
(opcional): O nome da classe Java inteiramente qualificado da classe persistente (ou interface); Se o atributo ausente, assume-se que o mapeamento para intidades no-POJO. table (opcional default para nomes de classes no qualificadas) O nome da sua tabela do banco de dados. discriminator-value (opcional default para o nome da classe): Um valor que distingue subclasses individuais, usadas para o comportamento polimorfico. Valores aceitos incluem null e not null mutable (opcional - valor default true): Especifica que instancias da classe so (ou no) mutveis schema (opcional): Sobrepe o nome do esquema especificado pelo elemento root <hibernate-mapping>. catalog (opcional): Sobrepe o nome do catlogo especificado pelo elemento root <hibernate-mapping>. proxy (opcional): Especifica um interface para ser utilizada pelos proxies de inicializao tardia. Voc pode especificar o nome da prpria classe. dynamic-update (opcional, valor default false): Especifica que o SQL de UPDATE deve ser gerado em tempo de execuo e conter apenas aquelas colunas cujos valores foram alterados. dynamic-insert (opcional, valor default false): Especifica que o SQL de INSERT deve ser gerado em tempo de execuo e conter apenas aquelas colunas cujos valores no esto nulos. select-before-update (opcional, valor default false): Especifica que o Hibernate never deve executar um SQL de UPDATE a no ser que com certeza um objeto est atualmente modificado. Em certos casos (atualmente, apenas quando um objeto transiente foi associado com uma nova sesso utilizando update()), isto significa que o Hibernate ira executar uma instruo SQL de SELECT adicional para determinar se um UPDATE necessrio nesse momento. polymorphism (opcional, default para implicit): Determina se deve ser utilizado a query polimorfica implicita ou explicitamente. where (opicional) especifica um comando SQL WHERE arbitrrio para ser usado quando da recuperao de objetos desta classe. persister (opcional): Espeicifca uma ClassPersister customizada. batch-size (opcional, valor default 1) especifica um "tamanho de lote" para a recuperao de instancias desta classe pelo identificador. optimistic-lock (octional, valor default version): Determina a estratgia de bloqueio. lazy (opcional): A recuperao tardia pode ser completamente desabilitada, setando lazy="false". entity-name (opcional, default para o nome da classe): O Hibernate3 permite uma classe ser mapeada multiplas vezes, (potencialmente,para diferentes tabelas), e permite mapeamentos de entidades que so representadas por Maps ou XML no nvel Java. Nestes casos, voc deve especificar um nome arbitrrio explicitamente para a entidade. Veja Seo 4.4, Dynamic models e Captulo 18, Mapeamento XML para maiores informaes. check (opcional): Uma expresso SQL utilizada para gerar uma constraint de verificao de mltiplas linhas para a gerao automtica do esquema. rowid (opcional): O Hibernate poder usar as assim chamadas ROWIDs em bancos de dados que a suportam. Por exemplo, no Oracle, o Hibernate pode utilizar a coluna extra rowid para atualizaes mais rpiname
53
Mapeamento O/R Bassico das se voc configurar esta opo para rowid. Um ROWID uma implementao que representa de maneira detalhada a localizao fsica de uma determinada tupla armazenado. subselect (optional): Maps an immutable and read-only entity to a database subselect. Useful if you want to have a view instead of a base table, but don't. See below for more information. subselect (opcional): Mapeia uma entidade imutavel e somente de leitura para um subconjunto do banco de dados. til se voc quiser ter uma view em vez de uma tabela. Veja abaixo para mais informaes. abstract (opcional): Utilizada para marcar superclasses abstratas em hierarquias <union-subclass>.
(20)
(21)
perfeitamente aceitvel para uma classe persitente nomeada ser uma interface. Voc dever ento declarar as classes implementadas desta interface utilizando o elemento <subclass>. Voc pode persistir qualquer classe de aninhada estatica. Voc dever especificar o nome da classe usando a forma padro, por exemplo: eg.Foo$Bar. Classes imutveis, mutable="false", no podem ser modificadas ou excludas pela aplicao. Isso permite ao Hibernate fazer alguns aperfeioamentos de performance. O atributo opcional proxy habilita a inicializao tardia das instncias persistentes da classe. O Hibernate ir retornar CGLIB proxies como implementado na interface nomeada. O objeto persistente atual ser carregado quando um mtodo do proxy for invocado. Veja "Proxies para Inicializao Lazy" abaixo. Polimorfismo implcito significa que instncias de uma classe sero retornada por uma query que d nome a qualquer superclasse ou interface implementada, ou a classe e as instancias de qualquer subclasse da classe ser retornada por umq query que nomeia a classe por si. Polimorfismo explcito significa que instancias da classe sero retornadas apenas por queries que explicitamente nomeiam a classe e que queries que nomeiam as classes iro retornar apenas instancias de subclasses mapeadas dentro da declarao <class> como uma <subclass> ou <joined-subclass>. Para a maioria dos casos, o valor default polymorphism="implicit", apropriado. Polimorfismo explicito til quando duas classes distintas esto mapeadas para a mesma tabela (isso permite um classe "peso leve" que contem um subconjunto de colunas da tabela). O atributo persister deixa voc customizar a estratgia de persistncia utilizada para a classe. Voc pode, por exemplo, especificar sua prrpia subclasse do org.hibernate.persister.EntityPersister ou voc pode criar uma implementao completamente nova da interface org.hibernate.persister.ClassPersister que implementa a persistncia atravs de, por exemplo, chamadas a stored procedeures, serializao de arquivos flat ou LDAP. Veja org.hibernate.test.CustomPersister para um exemplo simples (de "persistencia" para uma Hashtable). Observe que as configuraes dynamic-update e dynamic-insert no sao herdadas pelas subclasses e assim podem tambem ser especificadas em elementos <subclass> or <joined-subclass>. Estas configuraes podem incrementar a performance em alguns casos, mas pode realmente diminuir a performance em outras. Useas de forma bastante criteriosa. O uso de select-before-update geralmente ir diminuir a performance. Ela muito til para prevenir que uma trigger de atualizao no banco de dados seja ativada desnecessariamente, se voc reconectar um n de uma instancia desconectada em uma Session. Se voc ativar dynamic-update, voc ter de escolher a estratgia de bloqueio otimista:
version all
cverifica todas as colunas verifica as colunas modificadas, permitindo alguns updates concorrentes
dirty none
54
Ns recomendamos com muita enfase que voc utilize a verso e a hora das colunas para o bloqueio otimista com o Hibernate. Esta a melhor estratgia com respeito a performance e a nica estratgia que trata corretamente as modificaes efetuadas em instancias desconectadas (por exemplo, quando Session.merge() utilizado). No ha diferena entre uma view e uma tabela para o mapeamento do Hibernate, e como esperado isto transparente no nvel do banco de dados (observe que alguns bancos de dados no suportam views apropriadamente, especialmente com updates). Algumas vezes, voc quer utilizar uma view, ma sno pode cria-la no banco de dados (por exemplo, com um esquema legado). Neste caso, voc pode mapear uma entidade imutvel e de somente leitura, para uma dada expresso SQL, que representa um subselect:
<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>
Declare as tabelas para sincronizar com esta entidade, garantindo que o auto-flush ocorra corretamente, e que as queries para esta entidade derivada no retornem dados desatualizados. O <subselect> est disponvel tanto como um atributo como um elemento mapeado nested.
5.1.4. id
Classes mapeadas precisam declarar a coluna de chave primaria da tabela do banco de dados. Muitas classes iro tambem ter uma propriedade ao estilo Java-Beans declarando o identificador unico de uma instancia. O elemento <id> define o mapeamento desta propriedade para a chave primria.
<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> (1) (2) (3) (4) (5)
(5)
(opcional): O nome do identificador. type (opcional): Um nome que indica o tipo no Hibernate. column (opcional default para o a propridade name): O nome coluna chave primaria. unsaved-value (opcional - default para um valor "sensvel"): Uma propriedade de identificao que indica que a instancia foi novamente instanciada (unsaved), diferenciando de instancias desconectadas que foram salvas ou carregadas em uma sesso anterior. access (opcional - valor default property): A estratgia que o Hiberante deve utilizar para acessar o valor da propriedade
name
Se o atributo name no for declarado, assume-se que a classe no tem a propriedade de identificao. O atributo unsaved-value no mais necessrio no Hibernate 3.
55
H declarao alternativa <composite-id> permite o acesso a dados legados com chaves compostas. Ns desencorajamos fortemente o seu uso por qualquer pessoa. 5.1.4.1. Generator O elemento filho opcional <generator> nomeia uma classe Java usada para gerar identificadores unicos para instancias de uma classe persistente. Se algum parmetro requerido para configurar ou inicializar a instancia geradora, eles so passados utilizando o elemento <param>.
<id name="id" type="long" column="cat_id"> <generator class="org.hibernate.id.TableHiLoGenerator"> <param name="table">uid_table</param> <param name="column">next_hi_value_column</param> </generator> </id>
Todos os generators implementam a interface org.hibernate.id.IdentifierGenerator. Esta uma interface bem simples; algumas aplicaes podem prover sua prpria implementao esepecializada. Entretanto, o Hibernate disponibiliza um conjunto de implementaes internamente. H nomes de atalhos para estes generators prprios:
increment
gera identificadores dos tipos long, short ou int que so unicos apenas quando nenhum outro processo est inserindo dados na mesma tabela. No utilize em ambientes de cluster.
identity
suporta colunas de identidade em DB2, MySQL, MS SQL Server, Sybase e HypersonicSQL. O identificador retornado do tipo long, short ou int.
sequence
utiliza uma sequence em DB2, PostgreSQL, Oracle, SAP DB, McKoi ou um generator 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 default hibernate_unique_key e next_hi) como fonte para os valores hi. O algoritmo hi/lo gera identificadores que so nicos apenas para um banco de dados particular.
seqhilo
utiliza um algoritmo hi/lo para gerar de forma eficinete identificadores do tipo long, short ou int, a partir de uma sequence de banco de dados fornecida.
uuid
utiliza um algortimo UUID de 128-bits para gerar identificadores do tipo string, unicos em uma rede(o endereo IP utilizado). O UUID codificado como um string de digitos hexadecimais de tamanho 32.
guid
utiliza um string GUID gerado pelo banco de dados no MS SQL Server e MySQL.
native
seleciona entre identity, sequence ou hilo dependendo das capacidades do banco de dados utilizado.
assigned
deixa a aplicao definir um identificador para o objeto antes que o save() seja chamado. Esta a estratgia default se nenhum elemento <generator> especificado. Hibernate 3.2 cr2 56
select
retorna a chave primaria recuperada por uma trigger do banco de dados, selecionado uma linha pela chave nica e recuperando o valor da chave primria.
foreign
utiliza o identificador de um outro objeto associado. Normalmente utilizado em conjunto com uma associaa de chave primria do tipo <one-to-one>.
sequence-identity
a specialized sequence generation strategy which utilizes a database sequence for the actual value generation, but combines this with JDBC3 getGeneratedKeys to actually return the generated identifier value as part of the insert statement execution. This strategy is only known to be supported on Oracle 10g drivers targetted for JDK 1.4. Note comments on these insert statements are disabled due to a bug in the Oracle drivers. 5.1.4.2. Algoritmo Hi/lo Os geradores hilo e seqhilo fornecem duas implementaes alternativas do algoritmo hi/lo, uma soluo preferencial para a gerao de identificadores. A primeira implementao requer uma tabela especial do banco de dados para manter o proximo valor "hi" disponvel. A segunda utiliza uma seqncia do estilo Oracle (quando suportado).
<id name="id" type="long" column="cat_id"> <generator class="hilo"> <param name="table">hi_value</param> <param name="column">next_value</param> <param name="max_lo">100</param> </generator> </id>
<id name="id" type="long" column="cat_id"> <generator class="seqhilo"> <param name="sequence">hi_value</param> <param name="max_lo">100</param> </generator> </id>
Infelizemente, voce no pode utilizar hilo quando estiver fornecendo sia propria Connection para o Hibernate. Quando o Hibernate est usando um datasource do servidor de aplicaes para obter conexes suportadas com JTA, voc precisa configurar adequadamente o hibernate.transaction.manager_lookup_class. 5.1.4.3. UUID algorithm O UUID contem: o endereo IP, hora de inicio da JVM (com preciso de um quarto de segundo), a hora do sistema e um valor contador (unico dentro da JVM). No possivel obter o endereo MAC ou um endereo de memria do cdigo Java, assim este o melhor que pode ser feito sem utilizar JNI. 5.1.4.4. Colunas de identidade e sequencias Para bancos de dados que suportam colunas de identidade (DB2, MySQL, Sybase, MS SQL), voc pode utilizar uma gerao de chave identity. Para bancos de dados que suportam sequencias (DB2, Oracle, PostgreSQL, Interbase, McKoi, SAP DB) voce pode utilizar a gerao de chaves no estilo sequence. As duas estratgias requerem duas consultas SQL para inserir um novo objeto.
<id name="id" type="long" column="person_id"> <generator class="sequence">
57
Para desenvolvimento multi-plataforma, a estratgia native ir escolher entre as estratgias i identity, sequence e hilo, dependendo das capacidades do banco de dados utilizado. 5.1.4.5. Identificadores especificados Se voc quer que a aplicao especifique os identificadores (em vez do Hibernate ger-los) voc deve utilizar o gerador assigned. Este gerador especial ir utilizar o valor do identificador especificado para a propriedade de identificao do objeto. Este gerador usado quando a chave primaria a chave natural em vez de uma surrogate key. Este o comportamento padro se voc no especificar um elemento <generator>. Escolher o gerador assigned faz com que o Hibernate utilize unsaved-value="undefined", forando o Hibernate ir at o banco de dados para determinar se uma instncia est transiente ou desasociada, a menos que haja uma verso ou uma propriedade timestamp, ou voc pode definir Interceptor.isUnsaved(). 5.1.4.6. Chaves primrias geradas por triggers Apenas para sistemas legados (o Hibernate nao gera DDL com triggers).
<id name="id" type="long" column="person_id"> <generator class="select"> <param name="key">socialSecurityNumber</param> </generator> </id>
No exemplo acima, h uma nica propriedade com valor nomeada socialSecurityNumber definida pela classe, uma chave natural, e uma surrogate key nomeada person_id cujo valor gerado pro uma trigger.
5.1.5. composite-id
<composite-id name="propertyName" class="ClassName" mapped="true|false" access="field|property|ClassName"> node="element-name|." <key-property name="propertyName" type="typename" column="column_name"/> <key-many-to-one name="propertyName class="ClassName" column="column_name"/> ...... </composite-id>
Para tabelas com uma chave composta, voc pode mapear mltiplas propriedades da classe como propriedades de identificao. O elemento <composite-id> aceita o mapeamento da propriedade <key-property> e mapeamentos <key-many-to-one>como elements filhos.
<composite-id> <key-property name="medicareNumber"/> <key-property name="dependent"/> </composite-id>
58
Sua classe persistente precisa sobre escrever equals() e hashCode() para implementar identificadores compostos igualmente. E precisa tambm implementar Serializable. Infelizmente, esta soluo para um identificador composto significa que um objeto persistente seu prprio identificador. No h outro "handle" que o prprio objeto. Voc mesmo precisa instanciar uma instncia de outra classe persistente e preencher suas propriedades de identificao antes que voc possa dar um load() para o estado persistente associado com uma chave composta. Nos chamamos esta soluo de identificador composto embedded e no aconselhamos para aplicaes srias. Uma segunda soluo o que podemos chamar de identificador composto mapped quando a propriedades de identificao nomeadas dentro do elemento <composite-id> esto duplicadas tando na classe persistente como em uma classe de identificao separada.
<composite-id class="MedicareId" mapped="true"> <key-property name="medicareNumber"/> <key-property name="dependent"/> </composite-id>
No exemplo, ambas as classes de identificao compostas, MedicareId, e a prpria classe entidade tem propriedades nomeadas medicareNumber e dependent. A classe identificadora precisa sobrepor equals() e hashCode() e implementar Serializable. A desvantagem desta soluo obvia duplicao de cdigo. Os seguintes atributos o utilizados para especificar o mapeamento de um identificador composto: mapped (opcional, valor default false ): indica que um identificar composto mapeado usado, e que as propriedades de mapeamento contidas refere-se tanto a classe entidade e a classe de identificao composta. class (opcional, mas requerida para um identificar composto mapeado): A classe usada como um identificador composto.
mapped
Ns iremos descrever uma terceira e as vezes mais conveniente soluo, onde o identificador composto implementado como uma classe componente na Seo 8.4, . Componentes como identificadores compostos. Os atributos descritos abaixo aplicam-se apenas para esta soluo: (opcional, requerida para esta soluo): Uma propriedade do tipo componente que armazena o identificador composto (veja captulo 9) access (opcional - valor default property): A estartgia Hibernate que deve ser utilizada para acessar o valor da propriedade. class (opcional - valor default para o tipo de propriedade determiando por reflexo) : A classe componente utilizada como um identificador composto (veja a prxima sesso).
name
Esta terceira soluo, um componente de identificao, o que ns recomendamos para a maioria das aplicaes.
5.1.6. discriminator
O elemento <discriminator> necessrio para persistncia polimrfica utilizando a estratgia de mapeamento table-per-class-hierarchy e declara uma coluna discriminadora da tabela. A coluna discriminadora contem valores de marcao que dizem a camada de persistncia qual subclasse instanciar para uma linha particular. Um restrito conjunto de tipos que podem ser utilizados: string, character, integer, byte, short, boolean, yes_no, true_false.
<discriminator column="discriminator_column" type="discriminator_type"
(1) (2)
59
(4)
(5)
(opcional - valor default class) o nome da coluna discriminadora type (opcional - valor default string) o nome que indica o tipo Hibernate force (opcional - valor default false) "fora" o Hibernate a especificar valores discriminadores permitidos mesmo quando recuperando todas as instancias da classe root. insert (opcional - valor default para true) sete isto para false se sua coluna discriminadora tambm parte do identificador composto mapeado. (Diz ao Hibernate para no incluir a coluna em comandos SQL INSERTs). formula (opcional) uma expresso SQL arbitraria que e xecutada quando um tipo tem que ser avaliado. Permite discriminao baseada em contedo.
column
Valores atuais de uma coluna discriminada so especificados pelo atributo discriminator-value da <class> e elementos da <subclass>. O atributo force util (apenas) em tabelas contendo linhas com valores discriminadores "extras" que no esto mapeados para uma classe persistente. Este no geralmente o caso. Usando o atributo formula voce pode declarar uma expresso SQL arbitrria que sera utilizada para avaliar o tipo de uma linha :
<discriminator formula="case when CLASS_TYPE in ('a', 'b', 'c') then 0 else 1 end" type="integer"/>
(5)
(6)
(opcional - default a a propriedade name): O nome da coluna mantendo o numero da verso name: O nome da propriedade da classe persistente. type (opcional - valor default para integer): O tipo do numero da verso access (opcional - valor default property): A estratgia Hibernate que deve ser usada para acessar o valor da propriedade. unsaved-value (opcional valor default para undefined ): Um valor para a propriedade verso que indica que uma instancia uma nova instanciada (unsaved), distinguindo de instancias desconectadas que foram salvas ou carregadas em sesses anteriores. ((undefined especifica que o valor da propriedade de identificao deve ser utilizado). generated (optional - defaults to never): Specifies that this version property value is actually generated by the database. See the discussion of Seo 5.6, Generated Properties. generated (opcional - valor default never): Especifica que valor para a propriedade verso na verdade gerado pelo banco de dados.
column
60
Mapeamento O/R Bassico Veja a discusso da Seo Seo 5.6, Generated Properties. insert (opcional - valor default para true): Especifica se a coluna de verso deve ser includa no comando SQL de insert. Pode ser configurado como false se a coluna do banco de dados est definida com um valor default de 0.
(7)
Nmeros de verso podem ser dos tipos Hibernate long, integer, short, timestamp ou calendar. A verso de uma propriedade timestamp nunca deve ser nula para uma instancia desconectada, assim o Hibernate ir identificar qualquer instncia com uma verso nula ou timestamp como transiente, no importando qual estratgia para foi especificada para unsaved-value. Declarando uma verso nula ou a propriedade timestamp um caminho fcil para tratar problemas com reconexes transitivas no Hibernate, especialmente teis para pessoas utilizando identificadores assinaldados ou chaves compostas!
(4)
(5)
(6)
(opcional - valor default para a propriedade name): O nome da coluna que mantem o timestamp. O nome da propriedade no estilo JavaBeans do tipo Date ou Timestamp da classe persistente Java. access (opcional - valor default para property): A estretagia Hibernate que deve ser utilizada para acessar o valor da propriedade. unsaved-value (opcional - valor default null): Uma propriedade para a verso de que indica que uma instncia uma nova instanciada (unsaved), distinguindo-a de instancias desconectadas que foram salvas ou carregadas em sesses previas. (undefined especifica que um valor de propriedade de identificao deve ser utilizado) source (opcional - valor default para vm): De onde o Hibernate deve recuperar o valor timestamp? Do banco de dados ou da JVM corrente? Timestamps baseados em banco de dados levam a um overhead porque o Hibernate precisa acessar o banco de dados para determinar o "prximo valor", mas mais seguro para uso em ambientes de "cluster". Observe tambm, que nem todos Dialects suportam a recuperao do timestamp corrente do banco de dados, enquando outros podem no ser seguros para utilizao em bloqueios pela falta de preciso (Oracle 8 por exemplo) generated (opcional - valor default never): Especifica que o valor da propriedade timestamp gerado pelo banco de dados. Veja a discusso Seo 5.6, Generated Properties.
column name:
Observe que <timestamp> equivalente a <version type="timestamp">. E <timestamp source="db"> equivalente a <version type="dbtimestamp">.
5.1.9. property
O elemento <property> declara uma propriedade persistente de uma classe, no estilo JavaBean.
<property name="propertyName" column="column_name"
(1) (2)
61
type="typename" update="true|false" insert="true|false" formula="arbitrary SQL expression" 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|." index="index_name" unique_key="unique_key_id" length="L" precision="P" scale="S" /> name:
(3) (4) (4) (5) (6) (7) (8) (9) (10) (11)
(1) (2)
(3) (4)
(5)
(6)
(7)
(8)
(9) (10)
(11)
o nome da propriedade, iniciando com letra minscula. column (opcional - default para a propriedade name): o nome da coluna mapeada do banco de dados, Isto pode tambm ser especificado pelo(s) elemento(s) <column> aninhados. type (opcional): um nome que indica o tipo Hibernate. update, insert (opcional - valor default true): especifica que as colunas mapeadas devem ser incluidas nas instrues SQL de UPDATE e/ou INSERT . Setar ambas para to false permite uma propridade "derivada" pura cujo valor inicializado de outra propriedade que mapeie a mesma coluna(s) ou por uma trigger ou outra aplicao. formula (opcional): uma expresso SQL que definie o valor para uma propriedade calculada. Propriedades calculadas nao tem uma coluna de mapeamento para elas. access (opcional valor default property): A estratgia que o Hibernate deve utilizar para acessar o valor da propriedade lazy (opcional - valor default para false): Especifica que esta propriedade deve ser trazida de forma "lazy" quando a instancia da varivel acessada pela primeira vez (requer instrumentao bytecode em tempo de criao). unique (opcional): Habilita a gerao de DDL de uma unica constraint para as colunas. Assim, permite que isto seja o alvo de uma property-ref. not-null (opcional): Habilita a gerao de DDL de uma constraint de nulidade para as colunas. optimistic-lock (opcional - valor default true): Especifica se mudanas para esta propriedade requerem ou no bloqueio otimista. Em outras palavras, determina se um incremento de verso deve ocorrer quando esta propriedade est suja. generated (opcional - valor default never): Especifica que o valor da propriedade na verdade gerado pelo banco de dados. Veja a discusso da seo Seo 5.6, Generated Properties.
typename pode ser: 1. The name of a Hibernate basic type (eg. integer, string, character, date, timestamp, float, binary, serializable, object, blob). O nome do tipo basico do Hibernate (ex., integer, string, character, date, timestamp, float, binary, serializable, object, blob). O nome da classe Java com um tipo bsico default (ex. int, float, char, java.lang.String, java.util.Date, java.lang.Integer, java.sql.Clob). O nome da classe Java serializable O nome da classe de um tipo customizado (ex. com.illflow.type.MyCustomType).
2. 3. 4.
Se voc no especificar um tipo, o Hibernate ira utilizar reflexo sobre a propriedade nomeada para ter uma idia do tipo Hibernate correto. O Hibernate ira tentar interpretar o nome da classe retornada, usando as regras 2, 3 e 4 nesta ordem. Entretanto, isto no sempre suficiente Em certos casos, voc ainda ir necessitar do atributo type. (Por exemplo, para distinguir entre Hibernate.DATE ou Hibernate.TIMESTAMP, ou para espcificar uma tipo ciustomizado.) Hibernate 3.2 cr2 62
O atributo access permite voce controlar como o Hibernate ir acessar a propriedade em tempo de execuo. Por default, o Hibernate ir chamar os mtodos get/set das propriedades. Se voce especificar access="field", o Hibernate ira bipassar os metodos get/set, acessnado o campo diretamente, usando reflexo. Voc epode especificar sua prpria estratgia para acesso da propriedade criando uma classe que implemente a interface org.hibernate.property.PropertyAccessor. Um recurso especialmente poderoso o de propriedades derivadas. Estas propriedades so por definio readonly, e o valor da propriedade calculado em tempo de execuo. Voc declara este calculo como uma expresso SQL, que traduz para clausula SELECT de uma subquery daquery SQL que carrega a instancia:
<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 prpria tabela, atravs da no declarao de um alias para uma coluna particular ( customerId no exemplo dado). Observe tambem que voce pode usar o mapeamento de elemento aninhado <formula>, se voc no gostar de usar o atributo.
5.1.10. many-to-one
Uma associao ordinria para outra classe persistente declarada usando o elemento many-to-one. O modelo relacional uma associao many-to-one: a uma chave estrangeira de uma tabela referenciando a chave primaria da tabela destino.
<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" formula="arbitrary SQL expression" node="element-name|@attribute-name|element/@attribute|." embed-xml="true|false" index="index_name" unique_key="unique_key_id" foreign-key="foreign_key_name" /> name:
(1) (2) (3) (4) (5) (6) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15)
(1) (2)
(3)
(4)
(5)
O nome da propriedade. column (opcional): O nome da coluna foreign key. Isto pode tambm ser especificado atravs de elementos aninhados <column>. class (opcional default para o tipo de propriedade determinado pela reflexo). O nome da classe associada. cascade (opcional): Especifica quais operaes dever ser em cascata do objeto pai para o objeto associado. fetch (opcional - default para select): Escolhe entre recuperao outer-join ou recuperao seqencial.
63
(7)
(8)
(9)
(10) (11)
(12)
(13)
(14)
(opcional - valor default true): especifica que as colunas mapeadas dever ser incluidas em instrues SQL de UPDATE e/ou INSERT. Setando ambas para false voc permite uma associao "derivada" pura cujos valores so inicializados de algumas outras propriedades que mapeiam a mesma coluna ou por uma trigger ou outra aplicao. property-ref: (opcional) O nome da propriedade da classe associada que faz a juno desta foreign key. Se no especificada, a chave primaria da classe associada ser utilizada. access (opcional - valor default property): A estrategia que o Hibernate deve utilizar para acessar o valor da propriedade. unique (opcional): Habilita a gerao DDL de uma constraint unique para a coluna foreign-key. Alem disso, permite ser o alvo de uma property-ref. Isso torna a associao multipla efetivamente um para um. not-null (opcional): Habilita a gerao DDL de uma constraint de nulidade para as foreign keys. optimistic-lock (opcional - valor default true): Especifica se mudanas desta propriedade requerem ou no travamento otimista. Em outras palavras, determina se um incremento de verso deve ocorrer quando esta propriedade est suja. lazy(opcional valor default proxy): Por default, associaes de ponto unico so envoltas em um proxie. lazy="no-proxy" especifica que a propriedade deve ser trazida de forma tardia quando a instancia da varivel acessada pela primeira vez (requer instrumentao bytecode em tempo de criao) lazy="false" especifica que a associao ser sempre recuperada fortemente. not-found (opcional - valor default exception): Especifica como as foreign keys que referenciam linhas ausentes sero tratadas: ignore ir tratar a linha ausente como ama associaao de null entity-name (opcional): O nome da entidade da classe associada.
update, insert
Setar o valor do atributo cascade para qualquer valor significativo diferente de none ir propagar certas operaes ao objeto associado. Os valores significativos so os nomes das operaes bsicas do Hibernate, persist, merge, delete, save-update, evict, replicate, lock, refresh, assim como os valores especiais delete-orphan e all e combinaes de nomes de operaes separadas por vrgula, como por exemplo, cascade="persist,merge,evict" ou cascade="all,delete-orphan". Veja a seo Seo 10.11, Transitive persistence para uma explicao completa. Note que associaes valoradas simples (associaes muitos-pra-um, e um-pra-um) no suportam orphan delete. Uma tpica declarao muitos-pra-um se parece com esta:
<many-to-one name="product" class="Product" column="PRODUCT_ID"/>
O atributo property-ref deve apenas ser usado para mapear dados legados onde uma chave estrangeira se referencia a uma chave exclusiva da tabela associada que no seja chave primria. Este um modelo relacional desagradvel. Por exemplo, suponha que a classe Product tenha um nmero seqencial exclusivo, que no a chave primria. (O atributo unique controla a gerao de DDL do Hibernate com a ferramenta SchemaExport.)
<property name="serialNumber" unique="true" type="string" column="SERIAL_NUMBER"/>
Porm, isto obviamente no indicado, nunca. Se a chave exclusiva referenciada engloba mltiplas propriedades da entidade associada, voc deve mapear as propriedades referenciadas dentro de um elemento chamado <properties> Se a chave exclusiva referenciada a propriedade de um componente, voc pode especificar um caminho para a propriedade.
64
(1) (2) (3) (4) (5) (6) (7) (8) (9) (10)
(5) (6)
(7)
(8)
(9)
(10)
O nome da propriedade. class (opcional default para o tipo da propriedade definido via reflection): O nome da classe associada. cascade (opcional): Especifica qual operao deve ser cascateada do objeto pai para o objeto associado. constrained (opcional): Especifica que uma chave estrangeira constraint na chave primria da tabela mapeada referencia a tabela da classe associada, Esta opo afeta a ordem em queh save() e delete() so cascateadas, e determina se a associao pode ser substituda (isto tambm usado pela ferramenta schema export). fetch ((opcional valor default select): Escolhe entre outer-join fetching ou sequential select fetching. property-ref(opcional): O nome da propriedade da classe associada que ligada a chave primria desta classe. Se no for especificada, a chave primria da classe associada utilizada. access (opcional - valor default padro property): A estratgia que o Hibernate pode usar para acessar o valor da propriedade. formula (opcional): Quase todas associaes um-pra-um mapeiam para a chave primria da entidade dona. No caso raro, que no o caso, voc pode especificar uma outra coluna, colunas ou expresses para juntar utilizando uma formula SQL. (Veja org.hibernate.test.onetooneformula para exemplo). lazy (opcional valor default proxy): Por default, associaes single point so proxied. lazy="no-proxy" especifica que a propriedade deve ser fetched lazily quando o atributo acessado pela primeira vez (requer build-time bytecode instrumentation). lazy="false" especifica que a associao vai sempre ser avidamente fetched. Note que se constrained="false", proxing impossvel e o Hibernate vai vido fetch a associao! entity-name (opcional): O nome da entidade da classe associada.
Existem duas variedades de associaes um-pra-um: associaes de chave primria associaes de chave estrangeira exclusiva
Associaes de chave primria no necessitam de uma coluna extra de tabela; se duas linhas so relacionadas pela associao ento as duas linhas da tabela dividem a mesmo valor da chave primria. Assim, se voc quer que dois objetos sejam relacionados por uma associao de chave primria, voc deve ter certeza que eles so assinados com o mesmo valor identificador!
65
Para uma associao de chave primria, adicione os seguintes mapeamentos em Employee e Person, respectivamente.
<one-to-one name="person" class="Person"/>
Agora ns devemos assegurar que as chaves primrias de linhas relacionadas nas tabelas PERSON e EMPLOYEE so iguais. Ns usamos uma estratgia especial de gerao de identificador do Hibernate chamada foreign:
<class name="person" table="PERSON"> <id name="id" column="PERSON_ID"> <generator class="foreign"> <param name="property">employee</param> </generator> </id> ... <one-to-one name="employee" class="Employee" constrained="true"/> </class>
Uma nova instncia de Person salva recentemente ento assinada com o mesmo valor da chave primria da instncia de employee referenciada com a propriedade employee daquela Person. Alternativamente, uma chave estrangeira com uma unique constraint, de Employee para Person, pode ser expressa como:
<many-to-one name="person" class="Person" column="PERSON_ID" unique="true"/>
E esta associao pode ser feita de forma bi-direcional adicionando o seguinte no mapeamento de Person:
<one-to-one name"employee" class="Employee" property-ref="person"/>
5.1.12. natural-id
<natural-id mutable="true|false"/> <property ... /> <many-to-one ... /> ...... </natural-id>
Embora ns recomendemos o uso de surrogate keys como chaves primrias, voc deve ainda identificar chaves naturais para todas as entidades. Uma chave natural uma propriedade ou combinao de propriedades que exclusiva e no nula. Se no pude ser modificada, melhor ainda. Mapeie as propriedades da chave natural dentro do elemento <natural-id>. O Hibernate ir gerar a chave exclusiva necessria e as constraints de nullability , e seu mapeamento ser apropriadamente auto documentado. Ns recomendamos com enfase que voc implemente equals() e hashCode() para comparar as propriedades da chave natural da entidade. Este mapeamento no tem o objetivo de uso com entidades com chaves primrias naturais. mutable (opcional, valor defaultfalse): Por default, propriedades naturais identificadoras so consideradas imutveis (constante).
mutable
66
(1) (2)
(6)
(7)
(8)
O nome da propriedade. class (opcional valor default para o tipo de propriedade determinada por reflection): O nome da classe (filha) do componente. insert: As colunas mapeadas aparecem nos SQL de INSERTs? update: As colunas mapeadas aparecem nos SQL de UPDATEs? access (opcional valor default property): A estratgia que o Hibernate pode usar para acessar o valor da propriedade. lazy (opcional - valor default false): Especifica que este componente deve ser fetched lazily quando o atributo for acessado pela primeira vez (requer build-time bytecode instrumentation). optimistic-lock (opcional valor default true): Especifica que atualizaes para este componente requerem ou no aquisio de um lock otimista. Em outras palavras, determina se uma verso de incremento deve ocorrer quando esta propriedade estiver modificada. unique (opcional valor default false): Especifica que existe uma unique constraint em todas as colunas mapeadas do componente.
A tag filha <property> acrescenta a propriedade de mapeamento da classe filha para colunas de uma tabela. O elemento <component> permite um sub-elemento <parent> mapeie uma propriedade da classe do componente como uma referencia de volta para a entidade que o contm. O elemento <dynamic-component> permite que um Map possa ser mapeado como um componente onde os nomes das propriedades referem-se para as chaves no mapa, veja Seo 8.5, Componentes Dinmicos.
5.1.14. propriedades
O elemento <properties> permite a definio de um grupo com nome, lgico de propriedades de uma classe. O uso mais importante do construtor que este permite uma combinao de propriedades para ser o objetivo de uma property-ref. tambm um modo conveninente para definir uma unique constraint de mltiplas colunas.
<properties name="logicalName" insert="true|false" update="true|false" optimistic-lock="true|false" unique="true|false"
67
(5)
O nome lgico do agrupamento no o nome atual de propriedade. As colunas mapeadas aparecem nos SQL de INSERTs? update: As colunas mapeadas aparecem nos SQL de UPDATEs? optimistic-lock (opcional valor default true): Especifica que atualizaes para estes componentes requerem ou no aquisio de um lock otimista. Em outras palavras, determina se uma verso de incremento deve ocorrer quando estas propriedades estiverem modificadas. unique (opcional valor defautl false): Especifica que uma unique constraint existe em todas as colunas mapeadas do componente.
insert:
Ento ns podemos ter uma associao de dados herdados que referem a esta chave exclusiva da tabela Person, ao invs de se referirem a chave primria:
<many-to-one name="person" class="Person" property-ref="name"> <column name="firstName"/> <column name="initial"/> <column name="lastName"/> </many-to-one>
Ns no recomendamos o uso deste tipo de coisa fora do contexto de mapeamento de dados herdados.
68
name:
(3) (4)
O nome de classe completamente qualificada da subclasse. (opcional valor default o nome da classe): Um valor que distingue subclasses individuais. proxy (opcional): Especifica a classe ou interface que usar os proxies de inicializao atrasada. lazy (opcional, valor default true): Configurar lazy="false" desabilitar o uso de inicializao atrasada.
discriminator-value
Cada subclasse deve declarar suas prprias propriedades persistentes e subclasses. As propriedades <version> e <id> so configuradas para serem herdades da classe raiz. Cada subclasse numa hierarquia deve definir um nico discriminator-value. Se nenhum for especificado, o nome da classe Java completamente qualificada ser usada. Para informaes sobre mapeamento de heranas, veja o Captulo 9, Mapeamento de Herana.
5.1.16. joined-subclass
Alternativamente, cada subclasse pode ser mapeada para sua prpria tabela (Estratgia de mapeamento tableper-subclass). O estado herdado devolvido por associao com a tabela da superclasse. Ns usamos o elemento <joined-subclass>.
<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"> <key .... > <property .... /> ..... </joined-subclass> name:
O nome da classe completamente qualificada da subclasse. O nome da tabela da subclasse. proxy (opcional): Especifica a classe ou interface para usar os proxies de recuperao atrasada. lazy (opcional, valor default true): Fixanr lazy="false" desabilita o uso recuperao atrasada.
table:
A coluna discriminator requerida para esta estratgia de mapeamento. Porm, cada subclasse deve declarar uma coluna de tabela com o identificador do objeto usando o elemento <key>. O mapeamento no incio do captulo poderia ser re-escrito assim:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="eg"> <class name="Cat" table="CATS"> <id name="id" column="uid" type="long"> <generator class="hilo"/> </id> <property name="birthdate" type="date"/> <property name="color" not-null="true"/>
69
<property name="sex" not-null="true"/> <property name="weight"/> <many-to-one name="mate"/> <set name="kittens"> <key column="MOTHER"/> <one-to-many class="Cat"/> </set> <joined-subclass name="DomesticCat" table="DOMESTIC_CATS"> <key column="CAT"/> <property name="name" type="string"/> </joined-subclass> </class> <class name="eg.Dog"> <!-- mapping for Dog could go here --> </class> </hibernate-mapping>
5.1.17. union-subclass
Uma terceira opo mapear para tabelas apenas as classes concretas de uma hierarquia de heranas, (a estratgia table-per-concrete-class) onde cada tabela define todos os estados persistentes da classe, incluindo estados herdados. No Hibernate, no absolutamente necessrio mapear explicitamente como hierarquia de heranas. Voc pode simplesmente mapear cada classe com uma declarao <class> separada. Porm, se voc deseja usar associaes polimrficas (por exemplo: uma associao para a superclasse de sua hierarquia), voc precisa usar o mapeamento <union-subclass>.
<union-subclass name="ClassName" table="tablename" proxy="ProxyInterface" lazy="true|false" dynamic-update="true|false" dynamic-insert="true|false" schema="schema" catalog="catalog" extends="SuperclassName" abstract="true|false" persister="ClassName" subselect="SQL expression" entity-name="EntityName" node="element-name"> <property .... /> ..... </union-subclass> name:
(4)
O nome da subclasse completamente qualificada. table: O nome da tabela da subclasse. proxy (optional): Specifies a class or interface to use for lazy initializing proxies. proxy (opcional): Especifica a classe ou interface para usar os proxies de recuperao atrasada. lazy (optional, defaults to true): Setting lazy="false" disables the use of lazy fetching. lazy (opcional, valor default ptrue): Fixando lazy="false" desabilita o uso da recuperao atrasada.
A coluna discriminatria no requerida para esta estratgia de mapeamento. Para informaes sobre mapeamentos de herana, veja Captulo 9, Mapeamento de Herana.
70
5.1.18. join
Usando o elemento <join>>, possvel mapear propriedades de uma classe para vrias tabelas.
<join table="tablename" schema="owner" catalog="catalog" fetch="join|select" inverse="true|false" optional="true|false"> <key ... /> <property ... /> ... </join> table: (1) (2) (3) (4) (5) (6)
(5)
(6)
O nome da tabela associada. schema (opcional): Sobrepe o nome do esquema especificado pelo elemento raiz <hibernate-mapping>. catalog (opcional): Sobrepe o nome do catlogo especificado pelo elemento raiz<hibernate-mapping>. fetch(opcional valor default join): Se setado para join, o padro, o Hibernate ir usar um inner join para restaurar um join definido por uma classe ou suas subclasses e uma outer join para um join definido por uma subclasse. Se setado para select, ento o Hibernate ir usar uma seleo seqencial para um <join> definida numa subclasse, que ir ser emitido apenas se uma linha se concentrar para representar uma instncia da subclasse. Inner joins ir ainda ser usado para restaurar um <join> definido pela classe e suas superclasses. inverse (opcional valor default false): Se habilitado, o Hibernate no ir tentar inserir ou atualizar as propriedades definidas por este join. optional (opcional valor default false): Se habilitado, o Hibernate ir inserir uma linha apenas se as propriedades definidas por esta juno no forem nulas e ir sempre usar uma outer join para recuperar as propriedades.
Por exemplo, a informao de endereo para uma pessoa pode ser mapeada para uma tabela separada (enquanto preservando o valor da semntica de tipos para todas as propriedades):
<class name="Person" table="PERSON"> <id name="id" column="PERSON_ID">...</id> <join table="ADDRESS"> <key column="ADDRESS_ID"/> <property name="address"/> <property name="zip"/> <property name="country"/> </join> ...
Esta caracterstica til apenas para modelos de dados legados, ns recomendamos menos tabelas do que classes e um modelo de domnio bem granulado. Porm, til para ficar trocando entre estratgias de mapeamento de herana numa hierarquia simples, como explicado mais a frente.
5.1.19. key
Ns vimos que o elemento <key> surgiu algumas vezes at agora. Ele aparece em qualquer lugar que o elemento pai define uma juno para a nova tabela, e define a chave estrangeira para a tabela associada, que referencia a chave primria da tabela original. Hibernate 3.2 cr2 71
<key column="columnname" on-delete="noaction|cascade" property-ref="propertyName" not-null="true|false" update="true|false" unique="true|false" /> (1) (2) (3) (4) (5) (6)
(1)
(2)
(3)
(4)
(5)
(6)
. column (opcional): O nome da coluna da chave estrangeira. Isto tambm pode ser especificado por aninhamento de elemento(s) <column>. on-delete (opcional, valor default noaction): Especifica se a constraint da chave estrangeira no banco de dados esta habilitada para cascade delete . property-ref (opcional): Especifica que a chave estrangeira se refere a colunas que no so chave primria da tabela original. (Util para base de dados legadas.) not-null (opcional): Especifica que a coluna da chave estrangeira no aceita valores nulos (isto implcito em qualquer momento que a chave estrangeira tambm fizer parte da chave primria). update (optional): Specifies that the foreign key should never be updated (this is implied whenever the foreign key is also part of the primary key). update (opcional): Especifica que a chave estrangeira nunca deve ser atualizada (isto implcito em qualquer momento que a chave estrangeira tambm fizer parte da chave primria). unique (opcional): Especifica que a chave estrangeira deve ter uma constraint unique (sto implcito em qualquer momento que a chave estrangeira tambm fizer parte da chave primria).
Ns recomendamos que para sistemas que a performance de delete seja importante, todas as chaves deve ser definida on-delete="cascade", e o Hibernate ir usar uma constraint a nvel de banco de dados ON CASCADE DELETE, ao invs de muitas instrues DELETE. Esteja ciente que esta caracterstica um atalho da estratgia usual de optimistic locking do Hibernate para dados versionados. Os atributos not-null e update so teis quando estamos mapeamos uma associao unidirecional um para muitos. Se voc mapear uma asociao unidirecional um para muitos para uma chave estrangeira non-nullable, voc deve declarar a coluna chave usando <key not-null="true">.
<formula>SQL expression</formula>
O atributo column e formula podem at ser combinados dentro da mesma propriedade ou associao mapeando para expressar, por exemplo, associaes exticas.
<many-to-one name="homeAddress" class="Address"
72
5.1.21. import
Suponha que a sua aplicao tem duas classes persistentes com o mesmo nome, e voc no quer especificar o nome qualificado (do pacote) nas queries do Hibernate. As Classes devem ser "importadas" explicitamente, de preferncia contando com auto-import="true". Voc pode at importar classes e interfaces que no esto explicitamente mapeadas.
<import class="java.lang.Object" rename="Universe"/>
(1) (2)
O nome qualificado (do pacote) de qualquer classe Java. (opcional valor default, o nome da classe no qualificada): Um nome que pode ser usado numa linguagem de consulta.
rename
5.1.22. any
Existe mais um tipo de propriedade de mapeamento. O elemento de mapeamento <any> define uma associao polimrfica para classes de mltiplas tabelas. Este tipo de mapeamento sempre requer mais de uma coluna. A primeira coluna possui o tipo da entidade associada. A outra coluna que ficou possui o identificador. impossvel especificar uma restrio de chave estrangeira para este tipo de associao, assim isto claramente no visto como um caminho usual para associaes (polimrficas) de mapeamento. Voc deve usar este mapeamento apenas em casos muito especiais (exemplo: audit logs, dados de sesso do usurio, etc). O atributo meta-type permite a aplicao especificar um tipo adaptado que mapeia valores de colunas de banco de dados para classes persistentes que tem propriedades identificadoras do tipo especificado atravs do id-type. Voc deve especificar o mapeamento de valores do meta-type para nome de classes.
<any name="being" id-type="long" meta-type="string"> <meta-value value="TBL_ANIMAL" class="Animal"/> <meta-value value="TBL_HUMAN" class="Human"/> <meta-value value="TBL_ALIEN" class="Alien"/> <column name="table_name"/> <column name="id"/> </any>
<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 .... /> ..... (1) (2) (3) (4) (5) (6)
73
</any> name:
(4) (5)
(6)
o nome da propriedade. o tipo identificador. meta-type (opcional valor default string): Qualquer tipo que permitido para um mapeamento discriminador. cascade (opcional valor default none): o estilo do cascade. access (opcional valor default property): A estratgia que o hibernate deve usar para acessar o valor da propriedade. optimistic-lock (opcional - valor defaulttrue): Especifica que as atualizaes para esta propriedade requerem ou no aquisio da trava otimista. Em outras palavras, define se uma verso de incremento deve ocorrer se esta propriedade est modificada.
id-type:
Mapeamento O/R Bassico Voc pode escrever os seus prprios tipos de mapeamentos e implementar sua estratgia de converso adaptada, como voc ver adiante. Todos os tipos internos do hibernate exceto colees suportam semnticas nulas.
Tipos de mapeamentos de classes primitivas ou wrapper Java especificos (vendor-specific) para tipos de coluna SQL. Boolean, boolean, yes_no so todas codificaes alternativas para um boolean ou java.lang.Boolean do Java.
string
Tipos de mapeamento de java.util.Date e suas subclasses para os tipos SQL DATE, TIME e TIMESTAMP (ou equivalente).
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).
locale, timezone, currency
Tipos de mapeamentos de java.util.Locale, java.util.TimeZone e java.util.Currency para VARCHAR (ou VARCHAR2 no Oracle). Instncias de f Locale e Currency so mapeados para seus cdigos ISO. Instncias de TimeZone so mapeados para seu ID.
class
um tipo de mapeamento de java.lang.Class para VARCHAR (ou VARCHAR2 no Oracle). Uma Class mapeada pelo seu nome qualificado (completo).
binary
Maps long Java strings to a SQL CLOB or TEXT type. Mapeia strings longas de Java para um tipo SQL CLOB ou TEXT.
serializable
Mapeia tipos Java serializveis para um tipo binrio SQL apropriado. Voc pode tambm indicar o tipo serializable do Hibernate com o nome da classe ou interface Java serializvel que no padro para um tipo bsico.
clob, blob
Tipos de mapeamentos para as classes JDBC java.sql.Clob and java.sql.Blob. Estes tipos podem ser inconveniente para algumas aplicaes, visto que o objeto blob ou clob pode no ser reusado fora de uma transao. (Alm disso, o suporte de driver imcompleto e inconsistente.)
75
imm_date, imm_binary
imm_time,
imm_timestamp,
imm_calendar,
imm_calendar_date,
imm_serializable,
Mapeando tipos para o que geralmente so consideradas tipos mutveis de Java, onde o Hibernate faz determinadas otimizaes apropriadas somente para tipos imutveis de Java, e a aplicao trata o objeto como imutvel. Por exemplo, voc no deve chamar Date.setTime() para uma instncia mapeada como imm_timestamp. Para mudar o valor da propriedade, e ter a mudana feita persistente, a aplicao deve atribuir um novo objeto (nonidentical) propriedade. Identificadores nicos das entidades e colees podem ser de qualquer tipo bsico exceto binary, blob ou clob. (Identificadores compostos tambm so permitidos, veja abaixo.) Os tipos de valores bsicos tm suas constantes Type correspondentes definidas em org.hibernate.Hibernate. Por exemplo, Hibernate.STRING representa o tipo string.
implementar
um
Observe o uso da tag <column> para mapear uma propriedade para colunas mltiplas. As interfaces CompositeUserType, EnhancedUserType, UserCollectionType, e UserVersionType fornecem suporte para usos mais especializados. Voc pode mesmo fornecer parmetros a um UserType no arquivo de mapeamento. Para isto, seu UserType deve implementar a interface org.hibernate.usertype.ParameterizedType. Para fornecer parmetros 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 parmetro chamado default da Propriedade do passado a ele. Se voc usar freqentemente 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 tambm conter uma lista de valores default de parmetro se o tipo for parametrizado.
<typedef class="com.mycompany.usertypes.DefaultValueIntegerType" name="default_zero"> <param name="default">0</param> </typedef>
76
It is also possible to override the parameters supplied in a typedef on a case-by-case basis by using type parameters on the property mapping. Even though Hibernate's rich range of built-in types and support for components means you will very rarely need to use a custom type, it is nevertheless considered good form to use custom types for (non-entity) classes that occur frequently in your application. For example, a MonetaryAmount class is a good candidate for a CompositeUserType, even though it could easily be mapped as a component. One motivation for this is abstraction. With a custom type, your mapping documents would be future-proofed against possible changes in your way of representing monetary values.
Notice how associations are now specified using entity-name instead of class.
77
78
* column="COLOR" * not-null="true" */ public Color getColor() { return color; } void setColor(Color color) { this.color = color; } /** * @hibernate.set * inverse="true" * order-by="BIRTH_DATE" * @hibernate.collection-key * column="PARENT_ID" * @hibernate.collection-one-to-many */ public Set getKittens() { return kittens; } void setKittens(Set kittens) { this.kittens = kittens; } // addKitten not needed by Hibernate public void addKitten(Cat kitten) { kittens.add(kitten); } /** * @hibernate.property * column="SEX" * not-null="true" * update="false" */ public char getSex() { return sex; } void setSex(char sex) { this.sex=sex; } }
See the Hibernate web site for more examples of XDoclet and Hibernate.
79
@Transient Integer age; @Embedded private Address homeAddress; @OneToMany(cascade=CascadeType.ALL) @JoinColumn(name="CUSTOMER_ID") Set<Order> orders; // Getter/setter and business methods }
Note that support for JDK 5.0 Annotations (and JSR-220) is still work in progress and not completed. Please refer to the Hibernate Annotations module for more details.
(the default) - means that the given property value is not generated within the database.
- states that the given property value is generated on insert, but is not regenerated on subsequent updates. Things like created-date would fall into this category. Note that even thought Seo 5.1.7, version (optional) and Seo 5.1.8, timestamp (optional) properties can be marked as generated, this option is not available there...
insert always
- states that the property value is generated both on insert and on update.
80
The second mode is to supply a custom class which knows how to construct the CREATE and DROP commands. This custom class must implement the org.hibernate.mapping.AuxiliaryDatabaseObject interface.
<hibernate-mapping> ... <database-object> <definition class="MyTriggerDefinition"/> </database-object> </hibernate-mapping>
Additionally, these database objects can be optionally scoped such that they only apply when certain dialects are used.
<hibernate-mapping> ... <database-object> <definition class="MyTriggerDefinition"/> <dialect-scope name="org.hibernate.dialect.Oracle9Dialect"/> <dialect-scope name="org.hibernate.dialect.OracleDialect"/> </database-object> </hibernate-mapping>
81
The actual interface might be java.util.Set, java.util.Collection, java.util.List, java.util.Map, java.util.SortedSet, java.util.SortedMap or ... anything you like! (Where "anything you like" means you will have to write an implementation of org.hibernate.usertype.UserCollectionType.) A relao real pde ser java.util. Ajustar, java.util. Coleo, java.util. Lista, java.util. Mapa, java.util.SortedSet, java.util.SortedMap ou qualquer coisa que voc gosta! (Onde qualquer coisa voc gosta de meios voc ter que escrever uma execuo de org.hibernate.usertype.UserCollectionType.) Notice how we initialized the instance variable 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(), for example - Hibernate will actually replace the HashSet with an instance of Hibernate's own implementation of Set. Watch out for errors like this: Observao como ns inicializamos a varivel do exemplo com um exemplo de HashSet. Esta a mais melhor maneira inicializar propriedades avaliadas coleo de recentemente instantiated exemplos (non-persistentes). Quando voc faz o exemplo persistente - chamando a persistir (), por exemplo - Hibernate substituir realmente o HashSet com um exemplo de prpria execuo de Hibernate do jogo. O relgio para fora para erros gosta deste:
Cat cat = new DomesticCat(); Cat kitten = new DomesticCat(); .... Set kittens = new HashSet(); kittens.add(kitten); cat.setKittens(kittens); session.persist(cat); kittens = cat.getKittens(); // Okay, kittens collection is a Set (HashSet) cat.getKittens(); // Error!
The persistent collections injected by Hibernate behave like HashMap, HashSet, TreeMap, TreeSet or ArrayList, depending upon the interface type. As colees persistentes injetadas por Hibernate comportam-se como HashMap, HashSet, TreeMap, TreeSet ou ArrayList, dependendo em cima do tipo da relao. Collections instances have the usual behavior of value types. They are automatically persisted when referenced by a persistent object and automatically deleted when unreferenced. If a collection is passed from one persistent object to another, its elements might be moved from one table to another. Two entities may not share a reference to the same collection instance. Due to the underlying relational model, collection-valued properties do not support null value semantics; Hibernate does not distinguish between a null collection reference and an empty collection. Os exemplos das colees tm o comportamento usual de tipos do valor. So persistidos automaticamente quando referenced por um objeto persistente e suprimidos automaticamente quando unreferenced. Se Hibernate 3.2 cr2 82
Mapeamento de Colees.
uma coleo fosse passada de um objeto persistente a outro, seus elementos puderam ser movidos de uma tabela para outra. Duas entidades no podem compartilhar de uma referncia ao mesmo exemplo da coleo. Devido ao modelo relacional subjacente, as propriedades coleo-avaliadas no suportam a semntica nula do valor; Hibernate no distingue entre uma referncia nula da coleo e uma coleo vazia. You shouldn't have to worry much about any of this. Use persistent collections the same way you use ordinary Java collections. Just make sure you understand the semantics of bidirectional associations (discussed later). Voc no deve ter que preocupar-se muito sobre alguma deste. Usar colees persistentes a mesma maneira que voc usa colees ordinrias de Java. Certifica-se apenas voc para compreender a semntica das associaes bidirectional (discutidas mais tarde).
Apart from <set>, there is also <list>, <map>, <bag>, <array> and <primitive-array> mapping elements. The <map> element is representative:
<map name="propertyName" (1) table="table_name" (2) schema="schema_name" (3) lazy="true|extra|false" (4) inverse="true|false" (5) cascade="all|none|save-update|delete|all-delete-orphan|delet(6)e-orphan" sort="unsorted|natural|comparatorClass" (7) order-by="column_name asc|desc" (8) where="arbitrary sql where condition" (9) fetch="join|select|subselect" (10) batch-size="N" (11) access="field|property|ClassName" (12) optimistic-lock="true|false" (13) mutable="true|false" (14) node="element-name|." embed-xml="true|false" > <key .... /> <map-key .... /> <element .... /> </map>
(1) (2)
(3)
o nome da propriedade que uma coleo table (optional - defaults to property name) the name of the collection table (not used for one-to-many associations) (opcional - defeitos ao nome de propriedade) o nome da tabela da coleo (no usada para um--muitas associaes) schema (optional) the name of a table schema to override the schema declared on the root element (opcional) o nome de um schema da tabela para cancelar o schema declarou no elemento da raiz
name
83
Mapeamento de Colees.
(4)
(5)
(9)
(10)
(11) (12)
(13)
(14)
(optional - defaults to true) may be used to disable lazy fetching and specify that the association is always eagerly fetched, or to enable "extra-lazy" fetching where most operations do not initialize the collection (suitable for very large collections) inverse (optional - defaults to false) mark this collection as the "inverse" end of a bidirectional association cascade (optional - defaults to none) enable operations to cascade to child entities sort (optional) specify a sorted collection with natural sort order, or a given comparator class order-by (optional, JDK1.4 only) specify a table column (or columns) that define the iteration order of the Map, Set or bag, together with an optional asc or desc where (optional) specify an arbitrary SQL WHERE condition to be used when retrieving or removing the collection (useful if the collection should contain only a subset of the available data) fetch (optional, defaults to select) Choose between outer-join fetching, fetching by sequential select, and fetching by sequential subselect. batch-size (optional, defaults to 1) specify a "batch size" for lazily fetching instances of this collection. access (optional - defaults to property): The strategy Hibernate should use for accessing the collection property value. optimistic-lock (optional - defaults to true): Species that changes to the state of the collection results in increment of the owning entity's version. (For one to many associations, it is often reasonable to disable this setting.) mutable (optional - defaults to true): A value of false specifies that the elements of the collection never change (a minor performance optimization in some cases).
lazy
See the previous chapter for a full definition of the <key> element.
84
Mapeamento de Colees.
(1)
(1) (1)
(required): The name of the column holding the collection index values. base (optional, defaults to 0): The value of the index column that corresponds to the first element of the list or array.
column_name
(optional): The name of the column holding the collection index values. (optional): A SQL formula used to evaluate the key of the map. type (reguired): The type of the map keys.
column formula
(1) (2)(3)
(optional): The name of the foreign key column for the collection index values. formula (optional): A SQL formula used to evaluate the foreign key of the map key. class (required): The entity class used as the map key.
column
If your table doesn't have an index column, and you still wish to use List as the property type, you should map the property as a Hibernate <bag>. A bag does not retain its order when it is retrieved from the database, but it may be optionally sorted or ordered. There are quite a range of mappings that can be generated for collections, covering many common relational models. We suggest you experiment with the schema generation tool to get a feeling for how various mapping declarations translate to database tables.
85
Mapeamento de Colees.
(optional): The name of the column holding the collection element values. formula (optional): An SQL formula used to evaluate the element. type (required): The type of the collection element.
column
(5)
(6)
(7) (8)
(optional): The name of the element foreign key column. formula (optional): An SQL formula used to evaluate the element foreign key value. class (required): The name of the associated class. fetch (optional - defaults to join): enables outer-join or sequential select fetching for this association. This is a special case; for full eager fetching (in a single SELECT) of an entity and its many-to-many relationships to other entities, you would enable join fetching not only of the collection itself, but also with this attribute on the <many-to-many> nested element. unique (optional): Enable the DDL generation of a unique constraint for the foreign-key column. This makes the association multiplicity effectively one to many. not-found (optional - defaults to exception): Specifies how foreign keys that reference missing rows will be handled: ignore will treat a missing row as a null association. entity-name (optional): The entity name of the associated class, as an alternative to class. property-ref: (optional) The name of a property of the associated class that is joined to this foreign key. If not specified, the primary key of the associated class is used.
column
A bag containing integers (with an iteration order determined by the order-by attribute):
<bag name="sizes" table="item_sizes" order-by="size asc"> <key column="item_id"/> <element column="size" type="integer"/> </bag>
86
Mapeamento de Colees.
<array name="addresses" table="PersonAddress" cascade="persist"> <key column="personId"/> <list-index column="sortOrder"/> <many-to-many column="addressId" class="Address"/> </array>
An association from Product to Part requires existence of a foreign key column and possibly an index column to the Part table. A <one-to-many> tag indicates that this is a one to many association.
<one-to-many class="ClassName" not-found="ignore|exception" entity-name="EntityName" node="element-name" embed-xml="true|false" />
(1) (2)
(3)
(required): The name of the associated class. not-found (optional - defaults to exception): Specifies how cached identifiers that reference missing rows will be handled: ignore will treat a missing row as a null association. entity-name (optional): The entity name of the associated class, as an alternative to class.
class
Notice that the <one-to-many> element does not need to declare any columns. Nor is it necessary to specify the table name anywhere. Very important note: If the foreign key column of a <one-to-many> association is declared NOT NULL, you must Hibernate 3.2 cr2 87
Mapeamento de Colees. declare the <key> mapping not-null="true" or use a bidirectional association with the collection mapping marked inverse="true". See the discussion of bidirectional associations later in this chapter. This example shows a map of Part entities by name (where partName is a persistent property of Part). Notice the use of a formula-based index.
<map name="parts" cascade="all"> <key column="productId" not-null="true"/> <map-key formula="partName"/> <one-to-many class="Part"/> </map>
Allowed values of the sort attribute are unsorted, natural and the name of a class implementing java.util.Comparator. Sorted collections actually behave like java.util.TreeSet or java.util.TreeMap. 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 only available under JDK 1.4 or higher (it is implemented using LinkedHashSet or LinkedHashMap). This performs the ordering in the SQL query, not in memory.
<set name="aliases" table="person_aliases" order-by="lower(name) asc"> <key column="person"/> <element column="name" type="string"/> </set> <map name="holidays" order-by="hol_date, hol_name"> <key column="year_id"/> <map-key column="hol_name" type="string"/> <element column="hol_date type="date"/> </map>
Note that the value of the order-by attribute is an SQL ordering, not a HQL ordering! Associations may even be sorted by some arbitrary criteria at runtime using a collection filter().
sortedUsers = s.createFilter( group.getUsers(), "order by this.name" ).list();
88
Mapeamento de Colees.
As mudanas feitas somente de um lado da associao no so persistidas. Isto significa que o Hibernate tem duas representaes na memria para cada associao bidirecional, uma associao de A para B e uma outra associao de B para A. Isto mais fcil de compreender se voc pensa sobre o modelo do objetos do Java e como ns criamos um relacionamento muitos para muitos em Java:
// The category now "knows" about the relationship // The item now "knows" about the relationship // The relationship won't be saved! // The relationship will be saved
A outra ponta usada salvar a representao em memria base de dados. Voc pode definir uma associao bidirecional um para muitos atravs de uma associao um-para-muitos indicando as mesmas colunas da tabela que associao muitos-para-um e declarando a propriedade inverse="true" <class name="Parent"> <id name="id" column="parent_id"/> .... <set name="children" inverse="true">
89
Mapeamento de Colees.
<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 associao com inverse="true" no afeta as operaes em cascata, isso um conceito ortogonal.
Mas, se no houver nenhuma propriedade na classe filha, no podemos ver essa associao como verdadeiramente bidirecional (h uma informao disponvel em um lado da associao que no est disponvel no extremo oposto). Nesse caso, nos no podemos mapear a coleo usando inverse="true". Nos 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"/>
90
Mapeamento de Colees.
Veja que neste mapeamento, que um dos lado da associao, a coleo, responsvel pela atualizao da chave estrangeira. TODO: Isso realmente resulta em updates desnecessrios ?.
<map name="connections"> <key column="incoming_node_id"/> <map-key-many-to-many column="outgoing_node_id" class="Node"/> <many-to-many column="connection_id" class="Connection"/> </map>
A segunda maneira simplesmente remodelar a associao das classes da entidade. Esta a maneira que ns usamos de uma maneira geral. Uma alternativa final usar os elementos compostos, que ns discutiremos mais tarde.
6.3.5. Usando
o <idbag>
Se voc concorda com nossa viso que chaves compostas so uma coisa ruim e que as entidades devem ter identificadores sintticos (surrogate keys), ento voc deve estar achando um pouco estranho que as associaes muitos para muitos usando colees de valores que ns mostramos estejam mapeadas com chaves compostas! Bem, este ponto bastante discutvel; um simples tabela de associao no parece se beneficiar muito de uma surrogate key (entretanto uma coleo de valores compostos sim). Opcionalmente, o Hibernate prove uma maneira de mapear uma associao muitos para muitos com uma coleo de valores para uma tabela com uma surrogate key. O elemento <idbag> permite mapear um List (ou uma Collection com uma semntica de bag.
<idbag name="lovers" table="LOVERS"> <collection-id column="ID" type="long"> <generator class="sequence"/> </collection-id> <key column="PERSON1"/> <many-to-many column="PERSON2" class="Person" fetch="join"/> </idbag>
Como voc pode ver, um <idbag> possui um gerador de id sinttico, igual uma classe de entidade! Uma surrogate key diferente associada para cada elemento de coleo. Porm, o Hibernate no prove nenhum mecanismo para descobrir qual a surrogate key de uma linha em particular.
91
Mapeamento de Colees.
Note que o desempenho de atualizao de um <idbag> much melhor que um <bag> normal! O Hibernate pode localizar uma linha individual eficazmente e atualizar ou deletar individualmente, como um list, map ou set. Na implementao atual, a estratgia de gerao de identificador native no suportada para identificadores de coleo usando o <idbag>.
tem uma coleo de instancias de Child. Se cada Child tiver no mximo um parent, o mapeamento natural uma associao um para muitos:
<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>
92
Mapeamento de Colees.
<id name="id"> <generator class="sequence"/> </id> <set name="children" inverse="true"> <key column="parent_id"/> <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>
Uma outra alternativa, no caso de voc insistir que esta associao devea ser unidirecional, voc pode declarar a constraint como NOT NULL na tag <key> do mapeamento:
<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>
Por outro lado, se uma child puder ter os mltiplos parents, a associao apropriada muitos-para-muitos:
<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">
93
Mapeamento de Colees.
For more examples and a complete walk-through a parent/child relationship mapping, see Captulo 21, Example: Parent/Child. Para mais exemplos e um exemplo completo de mapeamento de relacionamento de mestre/ detalhe, veja Captulo 21, Example: Parent/Child. At mesmo o mapeamento de associaes mais exticas so possveis, ns catalogaremos todas as possibilidades no prximo captulo.
94
create table Person ( personId bigint not null primary key, addressId bigint not null ) create table Address ( addressId bigint not null primary key )
7.2.2. um para um
Uma associao unidirecional um-para-um em uma chave estrangeira quase idntica. A nica diferena a constraint unique 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>
95
Mapeamento de Associaes.
create table Person ( personId bigint not null primary key, addressId bigint not null unique ) create table Address ( addressId bigint not null primary key )
Uma associao unidirecional um-para-um na chave primaria geralmente usa um gerador de id special. ( Note que ns invertemos a direo da associao nesse exemplo).
<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>
create table Person ( personId bigint not null primary key ) create table Address ( personId bigint not null primary key )
create table Person ( personId bigint not null primary key ) create table Address ( addressId bigint not null primary key, personId bigint not null )
96
Mapeamento de Associaes.
Ns achamos que melhor usar uma tabela associativa para este tipo de associao.
create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId not null, addressId bigint not null primary key ) create table Address ( addressId bigint not null primary key )
97
Mapeamento de Associaes.
create table PersonAddress ( personId bigint not null primary key, addressId bigint not null ) create table Address ( addressId bigint not null primary key )
7.3.3. um para um
Uma associao unidirecional um-para-um em uma tabela associativa extremamente incomum, mas possvel.
<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>
create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique ) create table Address ( addressId bigint not null primary key )
create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personI create table Address ( addressId bigint not null primary key )
98
Mapeamento de Associaes.
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 coleo indexada ), voc precisa especificar a coluna chave estrangeira como not null, e deixar o Hibernate administrar a associao do lado da coleo para que seja mantido o ndice de cada elemento da coleo (fazendo com que o outro lado seja virtualmente inverso setando 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>
importante que voc defina not-null="true" no elemento <key> no mapeamento na coleo se a coluna de chave estrangeira for NOT NULL. No declare como not-null="true" apenas um elemento aninhado <column>, mas sim o elemento <key>.
99
Mapeamento de Associaes.
7.4.2. um para um
Uma associao bidirecional um para um em uma chave estrangeira bastante comum.
<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>
create table Person ( personId bigint not null primary key, addressId bigint not null unique ) create table Address ( addressId bigint not null primary key )
Uma associao bidirecional um para um em uma chave primria 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>
create table Person ( personId bigint not null primary key ) create table Address ( personId bigint not null primary key )
100
Mapeamento de Associaes.
</id> <set name="addresses" table="PersonAddress"> <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> <join table="PersonAddress" inverse="true" optional="true"> <key column="addressId"/> <many-to-one name="person" column="personId" not-null="true"/> </join> </class>
create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null, addressId bigint not null primary key ) create table Address ( addressId bigint not null primary key )
101
Mapeamento de Associaes.
create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique ) create table Address ( addressId bigint not null primary key )
create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personI create table Address ( addressId bigint not null primary key )
Ento ns podemos mapear uma associao para a instncia corrente (aquela com a effectiveEndDate igual a null) usando:
<many-to-one name="currentAccountInfo" property-ref="currentAccountKey"
102
Mapeamento de Associaes.
Em um exemplo mais complexo, imagine que a associao entre Employee e Organization mantida em uma tabela Employment cheia de dados histricos do trabalho. Ento a associao do funcionrio recentemente empregado (aquele com a startDate mais recente) deve ser mapeado desta maneira:
<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" column="orgId"/> </join>
Voc pode ser criativo com esta funcionalidade, mas geralmente mais prtico tratar estes tipos de casos, usando uma pesquisa HQL ou uma pesquisa por criteria.
103
public class Name { char initial; String first; String last; public String getFirst() { return first; } void setFirst(String first) { this.first = first; } public String getLast() { return last; } void setLast(String last) { this.last = last; } public char getInitial() { return initial; } void setInitial(char initial) { this.initial = initial; } }
Agora Name pode ser persistido como um componente de Person. Note que Name define mtodos getter e setter para suas propriedades persistentes, mas no necessita declarar nenhuma interface ou propriedades identifica-
104
A tabela pessoa teria as seguintes colunas pid, birthday, initial, first and last. Assim como todos os tipos por valor, componentes no suportam referencias cruzadas. Em outras palavras, duas pessoas poderiam possuir o mesmo nome, mas os dois objetos pessoa poderiam conter dois objetos nome independentes, apenas com "o mesmo" por valor. A semntica dos valores null de um componente so ad hoc. No recarregameno do contedo do objeto, O Hibernate assumira que se todas as colunas do componente so null, ento todo o componente null. Isto seria o certo para a maioria dos propsitos. As propriedades de um componente podem ser de qualquer tipo do Hibernate (collections, associaes muitos-para-um, outros componentes, etc). Componentes agrupados no devem ser consideros um uso extico. O Hibernate tem a inteno de suportar um modelo de objetos muito bem granulado. O elemento <component> permite um subelemento <parent> que mapeia uma propriedade da classe componente como uma referncia de volta para a entidade que a contm.
<class name="eg.Person" table="person"> <id name="Key" column="pid" type="string"> <generator class="uuid"/> </id> <property name="birthday" type="date"/> <component name="Name" class="eg.Name" unique="true"> <parent name="namedPerson"/> <!-- reference back to the Person --> <property name="initial"/> <property name="first"/> <property name="last"/> </component> </class>
Nota: se voc definir um Set de elementos compostos, muito importante implementar equals() e hashCode() corretamente.
105
Mapeamento de Componentes.
Elementos compostos podem conter componentes mas no collections. Se o seu elemento composto contiver componentes, use a tag <nested-composite-element> . Este um caso bastante extico uma collections de componentes que por si prpria possui componentes. Neste momento voc deve estar se perguntando se uma associao 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 semntica de persistncia ainda so diferentes. Por favor, veja que um mapeamento de elemento composto no suporta propriedades capazes de serem null se voc estiver usando um <set>. O Hibernate tem que usar cada valor das colunas para identificar um registro quando estiver deletando os objetos (no existe coluna chave primria separada na tabela de elemento composto), que no possvel com valores null. Voc tem que usar um dos dois, ou apenas propriedades no null em um elemento composto ou escolher uma <list>, <map>, <bag> ou <idbag>. Um caso especial de elemento composto um elemento composto com um elemento <many-to-one> aninhado. Um mapeamento como este permite voc a mapear colunas extras de uma tabela de associao de muitos-para-muitos para a classe de elemento composto. A seguinte associao de muitos-para-muitos de Order para um Item onde purchaseDate, price e quantity so propriedades da associao:
<class name="eg.Order" .... > .... <set name="purchasedItems" table="purchase_items" lazy="true"> <key column="order_id"> <composite-element class="eg.Purchase"> <property name="purchaseDate"/> <property name="price"/> <property name="quantity"/> <many-to-one name="item" class="eg.Item"/> <!-- class attribute is optional --> </composite-element> </set> </class>
Claro, que no pode ter uma referncia para purchase no outro lado, para a navegao da associao bidirecional. Lembre-se que componentes so tipos por valor e no permitem referncias compartilhadas. Uma classe Purchase simples pode estar no set de uma classe Order, mas ela no pode ser referenciada por Item no mesmo momento. At mesmo associaes ternrias (ou quaternria, etc) so possveis:
<class name="eg.Order" .... > .... <set name="purchasedItems" table="purchase_items" lazy="true"> <key column="order_id"> <composite-element class="eg.OrderLine"> <many-to-one name="purchaseDetails class="eg.Purchase"/> <many-to-one name="item" class="eg.Item"/> </composite-element> </set> </class>
Elementos compostos podem aparecer em pesquisas usando a mesma sintaxe assim como associaes para outras entidades.
106
Mapeamento de Componentes.
Nota: no Hibernate 3, o segundo requisito no absolutamente necessrio. Mas atenda ele de qualquer forma. Voc no pode usar um IdentifierGenerator para gerar chaves compostas. Ao invs disso o aplicativo deve gerenciar seus prprios identificadores. Use a tag <composite-id> (com elementos <key-property> aninhados) no lugar da declarao <id> de costume. Por exemplo, a classe OrderLine possui uma chave primria que depende da chave primria (composta) de Order.
<class name="OrderLine"> <composite-id name="id" class="OrderLineId"> <key-property name="lineId"/> <key-property name="orderId"/> <key-property name="customerId"/> </composite-id> <property name="name"/> <many-to-one name="order" class="Order" insert="false" update="false"> <column name="orderId"/> <column name="customerId"/> </many-to-one> .... </class>
Agora, qualquer chave estrangeira referenciando a tabela OrderLine tambm ser composta. Voc deve declarar isto em seus mapeamentos para outras classes. Uma associao para OrderLine seria mapeada dessa forma:
<many-to-one name="orderLine" class="OrderLine"> <!-- the "class" attribute is optional, as usual --> <column name="lineId"/> <column name="orderId"/> <column name="customerId"/> </many-to-one>
(Note que a tag <column> uma alternativa para o atributo column por toda a parte.) Uma associao many-to-many para many-to-many tambm 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>
Mapeamento de Componentes.
<set name="orderLines" inverse="true"> <key> <column name="orderId"/> <column name="customerId"/> </key> <one-to-many class="OrderLine"/> </set>
(O elemento <one-to-many>, como de costume, no declara colunas.) Se OrderLine possui uma collection, ela tambm tem uma chave estrangeira composta.
<class name="OrderLine"> .... .... <list name="deliveryAttempts"> <key> <!-- a collection inherits the composite key type --> <column name="lineId"/> <column name="orderId"/> <column name="customerId"/> </key> <list-index column="attemptId" base="1"/> <composite-element class="DeliveryAttempt"> ... </composite-element> </set> </class>
A semntica de um mapeamento <dynamic-component> idntica <component>. A vantagem deste tipo de mapeamento a habilidade de determinar as propriedades atuais do bean no momento de deploy, apenas editando o documento de mapeamento. A Manipulao em tempo de execuo do documento de mapeamento tambm possvel, usando o parser DOM. At melhor, voc pode acessar (e mudar) o metamodelo configuration-time do Hibernate atravs do objeto Configuration.
108
Adicionalmente, o Hibernate suporta uma quarta, um tipo levemente diferente de polimorfismo: polimorfismo implcito
possvel usar diferentes estratgias de mapeamento para diferentes ramificaes da mesma hierarquia de herana, e ento fazer uso do polimorfismo implcito para alcanar polimorfismo atravs da hierarquia completa. De qualquer forma, O Hibernate no suporta a mistura de mapeamentos <subclass>, and <joined-subclass> e <union-subclass> dentro do mesmo elemento raiz <class>. possvel usar junto s estratgias tabela por hierarquia e a tabela por subclasse, abaixo do mesmo elemento <class>, combinando os elementos <subclass> e <join> (veja abaixo). possvel definir mapeamentos subclass, union-subclass, e joined-subclass em documentos de mapeamento separados, diretamente abaixo de hibernate-mapping. Isso permite a voc estender uma hierarquia de classes apenas adicionando um novo arquivo de mapeamento. Voc deve especificar um atributo extends no mapeamento da subclasse, nomeando uma superclasse previamente mapeada. Nota: Anteriormente esta caracterstica fazia o ordenamento dos documentos de mapeamento importantes. Desde o Hibernate3, o ordenamento dos arquivos de mapeamento no importa quando usamos a palavra chave extends. O ordenamento dentro de um arquivo de mapeamento simples ainda necessita ser definido como superclasse antes de subclasse.
<hibernate-mapping> <subclass name="DomesticCat" extends="Cat" discriminator-value="D"> <property name="name" type="string"/> </subclass> </hibernate-mapping>
109
Mapeamento de Herana
<subclass name="CashPayment" discriminator-value="CASH"> ... </subclass> <subclass name="ChequePayment" discriminator-value="CHEQUE"> ... </subclass> </class>
Exactly one table is required. There is one big limitation of this mapping strategy: columns declared by the subclasses, such as CCTYPE, may not have NOT NULL constraints.
Quatro tabelas so necessrias. As trs tabelas subclasses possuem associao de chave primria para a tabela de superclasse (ento o modelo relacional atualmente uma associao de um-para-um).
110
Mapeamento de Herana
<subclass name="CashPayment" discriminator-value="CASH"> <join table="CASH_PAYMENT"> <key column="PAYMENT_ID"/> ... </join> </subclass> <subclass name="ChequePayment" discriminator-value="CHEQUE"> <join table="CHEQUE_PAYMENT" fetch="select"> <key column="PAYMENT_ID"/> ... </join> </subclass> </class>
The optional fetch="select" declaration tells Hibernate not to fetch the ChequePayment subclass data using an outer join when querying the superclass. A declarao opcional fetch=select diz ao Hibernate para no buscar os dados da subclasse ChequePayment, quando usar um outer join pesquisando pela superclasse.
9.1.4. . Misturando tabela por hierarquia de classes com tabela por subclasse
Voc pode at mesmo misturar a estratgia de tabela por hierarquia e tabela por subclasse usando esta abordagem:
<class name="Payment" table="PAYMENT"> <id name="id" type="long" column="PAYMENT_ID"> <generator class="native"/> </id> <discriminator column="PAYMENT_TYPE" type="string"/> <property name="amount" column="AMOUNT"/> ... <subclass name="CreditCardPayment" discriminator-value="CREDIT"> <join table="CREDIT_PAYMENT"> <property name="creditCardType" column="CCTYPE"/> ... </join> </subclass> <subclass name="CashPayment" discriminator-value="CASH"> ... </subclass> <subclass name="ChequePayment" discriminator-value="CHEQUE"> ... </subclass> </class>
Para qualquer uma dessas estratgias de mapeamento, uma associao polimrfica para a classe raiz Payment deve ser mapeada usando <many-to-one>.
<many-to-one name="payment" column="PAYMENT_ID" class="Payment"/>
111
Mapeamento de Herana
<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>
Trs tabelas esto envolvidas para as subclasses. Cada tabela define colunas para todas as propriedades da classe, incluindo propriedades herdadas. A limitao dessa abordagem que se uma propriedade mapeada na superclasse, o nome da coluna deve ser o mesmo em todas as tabelas das subclasses. (Ns devemos melhorar isto em um futuro release do Hibernate). A estratgia do gerador de identidade no permitida em unio de subclasses(union-subclass) herdadas, na verdade a fonte de chave primria deve ser compartilhada atravs de todas subclasses unidas da hierarquia. Se sua superclasse abstrata, mapeie ela com abstract="true". Claro, que se ela no for abstrata, uma tabela (padro para PAYMENT no exemplo acima) adicional necessria para segurar as instncias da superclasse.
Veja que em nenhum lugar mencionamos a interface Payment explicitamente. Tambm preste ateno que propriedades de Payment so mapeadas em cada uma das subclasses. Se voc quer evitar duplicao, considere usar entidades de XML (ex. (e.g. [ <!ENTITY allproperties SYSTEM "allproperties.xml"> ] na declarao do DOCTYPE e &allproperties; no mapeamento). A desvantagem dessa abordagem que o Hibernate no gera UNIONs SQL quando executa pesquisas polimrficas. Para essa estratgia, uma associao polimrfica para Payment geralmente mapeada usando <any>.
112
Mapeamento de Herana
<any name="payment" meta-type="string" id-type="long"> <meta-value value="CREDIT" class="CreditCardPayment"/> <meta-value value="CASH" class="CashPayment"/> <meta-value value="CHEQUE" class="ChequePayment"/> <column name="PAYMENT_CLASS"/> <column name="PAYMENT_ID"/> </any>
Mais uma vez, ns no mencionamos Payment explicitamente. Se ns executarmos uma pesquisa em cima da interface Payment por exemplo, from Payment o Hibernate retorna automaticamente instncias de CreditCardPayment (e suas subclasses, desde que elas tambm implementem Payment), CashPayment e ChequePayment mas no as instncias de NonelectronicTransaction.
9.2. Limitaes
Existem certas limitaes para a abordagem do "polimorfismo implcito" comparada com a estratgia de mapeamento da tabela por classe concreta. Existe uma limitao um tanto menos restritiva para mapeamentos <union-subclass>. A tabela seguinte demonstra as limitaes do mapeamento de tabela por classe concreta e do polimorfismo implcito no Hibernate.
113
Mapeamento de Herana
Tabela 9.1. Features of inheritance mappings Estratgia de Herana table per classhierarchy table per subclass muitos-pa- um-para-um Po- ra-um Polimrfico limrfico um-para-muitos Polimrfico
<one-to-ma ny>
muitos-para-muitos Polimrfico
<many-to-m any>
Joins polimrficos
<many-to-o ne>
<one-to-on e>
<many-to-o ne>
<one-to-on e>
<one-to-ma ny>
<many-to-m any>
from Payment p
table per <many-to-o <one-to-on concretene> e> class (union-subc lass) table per concrete class (implicit polymorphism)
<any>
<one-to-ma ny>
<many-to-m any>
from Payment p
(for
inverse="true"
not suppor- not suppor- <many-to-a s.createCr from Payted ted ny> itement p
ria(Paymen t.class).a dd( Restrictions.idEq(i d) ).uniqueRe sult()
114
Agora iremos discutir os estados e suas transies ( e os mtodos do Hibernate que disparam uma transio) em mais detalhes.
Se Cat possui um identificador gerado, o identificador gerado e atribudo a cat quando save() for chamada. Se Cat possuir um identificador Associado, ou uma chave composta, o identificador deve ser atribudo insHibernate 3.2 cr2 115
Trabalhando com objetos tncia de cat antes que save() seja chamado. Pode-se usar tambm persist() ao invs de save(), com a semntica definada no novo esboo do EJB3. Alternativamente, pode-se atribuir o identificador usando uma verso sobrecarregada de save().
DomesticCat pk = new DomesticCat(); pk.setColor(Color.TABBY); pk.setSex('F'); pk.setName("PK"); pk.setKittens( new HashSet() ); pk.addKitten(fritz); sess.save( pk, new Long(1234) );
Se o objeto persistido possuir objetos associados (e.g. a coleo kittens no exemplo anterior), esses objetos podem ser tornar persistente em qualquer ordem que se queira ao menos que se tenha uma restrio NOT NULL em uma coluna de chave estrangeira. Nunca h risco de violao de restries de chave estrangeira. Assim, pode-se violar uma restrio NOT NULL se save() for usada nos objetos em uma ordem errada. Geralmente voc no deve se importar com esses detalhes, muito provavelmente se usar a caracterstica de persistncia transitiva do Hibernate para salvar os objetos associados automaticamente. Ento, enquanto uma restrio NOT NULL no ocorrer Hibernate tomar conta de tudo. Persistncia transitiva ser discutida futuramente nesse captulo.
// you need to wrap primitive identifiers long id = 1234; DomesticCat pk = (DomesticCat) sess.load( DomesticCat.class, new Long(id) );
Alternatively, you can load state into a given instance: Alternativamente, pode-se carregar um estado em uma instncia dada:
Cat cat = new DomesticCat(); // load pk's state into cat sess.load( cat, new Long(pkId) ); Set kittens = cat.getKittens();
Repare que load() ir lanar uma exceo irrecupervel se no houver na tabela no banco de dados um registro que combine. Se a classe for mapeada com um proxy, load() simplesmente retorna um proxy no inicializado e realmente no chamar o banco de dados at que um mtodo do proxy seja invocado. Esse comportamento muito til se deseja-se criar uma associao com um objeto sem que realmente o carregue do bando de dados. Isto tambm permite que sejam carregadas mltiplas instncias como um grupo se batch-size estiver para o mapeamento da classe. Se voc no tiver certeza da existencia do registro no banco, voc deve usar o metodo get(), que consulta o banco imediantamente e retorna um null se no existir o registro.
Cat cat = (Cat) sess.get(Cat.class, id); if (cat==null) { cat = new Cat(); sess.save(cat, id);
116
} return cat;
Tambm pode-se carregar um objeto usando SELECT ... FOR UPDATE, usando um LockMode. Veja a documentao da API para maiores informaes.
Cat cat = (Cat) sess.get(Cat.class, id, LockMode.UPGRADE);
Note that any associated instances or contained collections are not selected FOR UPDATE, unless you decide to specify lock or all as a cascade style for the association. O recarregamento de um objeto e todas as suas colees possvel a qualquer momento, usando o mtodo refresh(). Util quando as triggers do banco de dados so usados para inicializar algumas propriedades do objeto.
sess.save(cat); sess.flush(); //force the SQL INSERT sess.refresh(cat); //re-read the state (after the trigger executes)
Uma importante questo geralmente aparece neste ponto: O quanto Hibernate carrega do banco de dados e quantos SQL SELECT ele ir usar? Isto depende da estratgia de recuperaousada e explicada na Seo 19.1, Estratgias de Fetching.
10.4. Consultando
Se o identificador do objeto que se est buscando no for conhecido, uma consulta ser necessria. O Hibernate suporta uma linguagem de consulta (HQL) orientada a objetos fcil mas poderosa. Para criao via programao de consultas, o Hibernate suporta caractersticas sofisticadas de consulta por Critrio e Exemplo (QBCe QBE). Pode-se tambm expressar a consulta por meio de SQL nativa do banco de dados, com suporte opcional do Hibernate para converso do conjunto de reultados em objetos.
117
Geralmente uma consulta executada ao invocar list(), o resultado da consulta ser carregado completamente em uma coleo na memria. Instncias de entidades recuperadas por uma consulta esto no estado persistente. O uniqueResult() oferece um atalho se voc souber de previamente que a consulta retornar apenas um nico objeto. Repare que consultas que fazem uso de buscas de colees de forma ansiosa (eager) geralmente retornam duplicatas dos objetos raiz ( mas com suas colees inicializadas ). Pode-se filtrar estas duplicatas atravs de um simples Set. 10.4.1.1. Interagindo com resultados Ocasionalmente, deves-se ser capaz de atingir performances melhores com a execuo de consultas usando o mtodo iterate(). Geralmente isso ser o caso esperado apenas se as instncias dos entidades reais retornadas pela consulta j estiverem na sesso ou no cach de segundo nvel. Caso elas ainda no tenham sido armazenadas, iterate() ser mais devagar do que list() e pode ser necessrio vrios acessos ao banco de dados para um simples consulta, geralmente 1 para a seleo inicial que retorna apenas identificadores, e n consultas adicionais para inicializar as instncias reais.
// 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; } }
10.4.1.2. Consultas que retornam tuplas Algumas vezes as consultas do Hibernate retornam tuplas de objetos, nesse caso cada tupla retornada como um array:
Iterator kittensAndMothers = sess.createQuery( "select kitten, mother from Cat kitten join kitten.mother mother") .list() .iterator(); while ( kittensAndMothers.hasNext() ) { Object[] tuple = (Object[]) kittensAndMothers.next(); Cat kitten = tuple[0]; Cat mother = tuple[1]; .... }
10.4.1.3. Resultados escalares Consultas devem especificar uma propriedade da classe na clausula select. Elas tambm podem chamar funes SQL de agregaos. Propriedades ou agregaes so considerados resultados agregados ( e no entidades no estado persistente).
Iterator results = sess.createQuery( "select cat.color, min(cat.birthdate), count(cat) from Cat cat " + "group by cat.color") .list() .iterator();
118
while ( results.hasNext() ) { Object[] row = (Object[]) results.next(); Color type = (Color) row[0]; Date oldest = (Date) row[1]; Integer count = (Integer) row[2]; ..... }
10.4.1.4. Bind parameters Methods on Query are provided for binding values to named parameters or JDBC-style ? parameters. Contrary to JDBC, Hibernate numbers parameters from zero. Named parameters are identifiers of the form :name in the query string. The advantages of named parameters are: named parameters are insensitive to the order they occur in the query string they may occur multiple times in the same query they are self-documenting
//named parameter (preferred) Query q = sess.createQuery("from DomesticCat cat where cat.name = :name"); q.setString("name", "Fritz"); Iterator cats = q.iterate();
//positional parameter Query q = sess.createQuery("from DomesticCat cat where cat.name = ?"); q.setString(0, "Izi"); Iterator cats = q.iterate();
//named parameter list List names = new ArrayList(); names.add("Izi"); names.add("Fritz"); Query q = sess.createQuery("from DomesticCat cat where cat.name in (:namesList)"); q.setParameterList("namesList", names); List cats = q.list();
10.4.1.5. Pagination If you need to specify bounds upon your result set (the maximum number of rows you want to retrieve and / or the first row you want to retrieve) you should use methods of the Query interface:
Query q = sess.createQuery("from DomesticCat cat"); q.setFirstResult(20); q.setMaxResults(10); List cats = q.list();
Hibernate knows how to translate this limit query into the native SQL of your DBMS. 10.4.1.6. Scrollable iteration If your JDBC driver supports scrollable ResultSets, the Query interface may be used to obtain a ScrollableResults object, which allows flexible navigation of the query results.
Query q = sess.createQuery("select cat.name, cat from DomesticCat cat " + "order by cat.name"); ScrollableResults cats = q.scroll(); if ( cats.first() ) { // find the first name on each page of an alphabetical list of cats by name
119
firstNamesOfPages = new ArrayList(); do { String name = cats.getString(0); firstNamesOfPages.add(name); } while ( cats.scroll(PAGE_SIZE) ); // Now get the first page of cats pageOfCats = new ArrayList(); cats.beforeFirst(); int i=0; while( ( PAGE_SIZE > i++ ) && cats.next() ) pageOfCats.add( cats.get(1) ); } cats.close()
Note that an open database connection (and cursor) is required for this functionality, use setMaxResult()/setFirstResult() if you need offline pagination functionality. 10.4.1.7. Externalizing named queries You may also define named queries in the mapping document. (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>
Note that the actual program code is independent of the query language that is used, you may also define native SQL queries in metadata, or migrate existing queries to Hibernate by placing them in mapping files. Also note that a query declaration inside a <hibernate-mapping> element requires a global unique name for the query, while a query declaration inside a <class> element is made unique automatically by prepending the fully qualified name of the class, for example eg.Cat.ByNameAndMaximumWeight.
The returned collection is considered a bag, and it's a copy of the given collection. The original collection is not modified (this is contrary to the implication of the name "filter", but consistent with expected behavior).
120
Observe that filters do not require a from clause (though they may have one if required). Filters are not limited to returning the collection elements themselves.
Collection blackKittenMates = session.createFilter( pk.getKittens(), "select this.mate where this.color = eg.Color.BLACK.intValue") .list();
Even an empty filter query is useful, e.g. to load a subset of elements in a huge collection:
Collection tenKittens = session.createFilter( mother.getKittens(), "") .setFirstResult(0).setMaxResults(10) .list();
The Criteria and the associated Example API are discussed in more detail in Captulo 15, Consultas por critrios.
List cats = session.createSQLQuery( "SELECT {cat}.ID AS {cat.id}, {cat}.SEX AS {cat.sex}, " + "{cat}.MATE AS {cat.mate}, {cat}.SUBCLASS AS {cat.class}, ... " + "FROM CAT {cat} WHERE ROWNUM<10", "cat", Cat.class ).list()
SQL queries may contain named and positional parameters, just like Hibernate queries. More information about native SQL queries in Hibernate can be found in Captulo 16, Native SQL.
121
Trabalhando com objetos (discussed later in this chapter). There is no need to call a particular method (like update(), which has a different purpose) to make your modifications persistent. So the most straightforward way to update the state of an object is to load() it, and then manipulate it directly, while the Session is open:
DomesticCat cat = (DomesticCat) sess.load( Cat.class, new Long(69) ); cat.setName("PK"); sess.flush(); // changes to cat are automatically detected and persisted
Sometimes this programming model is inefficient since it would require both an SQL SELECT (to load an object) and an SQL UPDATE (to persist its updated state) in the same session. Therefore Hibernate offers an alternate approach, using detached instances. Note that Hibernate does not offer its own API for direct execution of UPDATE or DELETE statements. Hibernate is a state management service, you don't have to think in statements to use it. JDBC is a perfect API for executing SQL statements, you can get a JDBC Connection at any time by calling session.connection(). Furthermore, the notion of mass operations conflicts with object/relational mapping for online transaction processingoriented applications. Future versions of Hibernate may however provide special mass operation functions. See Captulo 13, Processamento de lotes for some possible batch operation tricks.
If the Cat with identifier catId had already been loaded by secondSession when the application tried to reattach it, an exception would have been thrown. Use update() if you are sure that the session does not contain an already persistent instance with the same identifier, and merge() if you want to merge your modifications at any time without consideration of the state of the session. In other words, update() is usually the first method you would call in a fresh session, ensuring that reattachment of your detached instances is the first operation that is executed. The application should individually update() detached instances reachable from the given detached instance if and only if it wants their state also updated. This can be automated of course, using transitive persistence, see Seo 10.11, Transitive persistence. The lock() method also allows an application to reassociate an object with a new session. However, the detached instance has to be unmodified!
//just reassociate: sess.lock(fritz, LockMode.NONE);
122
//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 that lock() can be used with various LockModes, see the API documentation and the chapter on transaction handling for more information. Reattachment is not the only usecase for lock(). Other models for long units of work are discussed in Seo 11.3, Controle de concorrncia otimista.
// update existing state (cat has a non-null id) // save the new instance (mate has a null id)
The usage and semantics of saveOrUpdate() seems to be confusing for new users. Firstly, so long as you are not trying to use instances from one session in another new session, you should not need to use update(), saveOrUpdate(), or merge(). Some whole applications will never use either of these methods. Usually update() or saveOrUpdate() are used in the following scenario: the application loads an object in the first session the object is passed up to the UI tier some modifications are made to the object the object is passed back down to the business logic tier the application persists these modifications by calling update() in a second session does the following:
saveOrUpdate()
if the object is already persistent in this session, do nothing if another object associated with the session has the same identifier, throw an exception if the object has no identifier property, save() it if the object's identifier has the value assigned to a newly instantiated object, save() it if the object is versioned (by a <version> or <timestamp>), and the version property value is the same value assigned to a newly instantiated object, save() it otherwise update() the object
and merge() is very different: if there is a persistent instance with the same identifier currently associated with the session, copy the state of the given object onto the persistent instance if there is no persistent instance currently associated with the session, try to load it from the database, or create a new persistent instance the persistent instance is returned 123
Trabalhando com objetos the given instance does not become associated with the session, it remains detached
You may delete objects in any order you like, without risk of foreign key constraint violations. It is still possible to violate a NOT NULL constraint on a foreign key column by deleting objects in the wrong order, e.g. if you delete the parent, but forget to delete the children.
The ReplicationMode determines how replicate() will deal with conflicts with existing rows in the database.
ReplicationMode.IGNORE
- ignore the object when there is an existing database row with the same identifi-
er - overwrite any existing database row with the same identifier ReplicationMode.EXCEPTION - throw an exception if there is an existing database row with the same identifier ReplicationMode.LATEST_VERSION - overwrite the row if its version number is earlier than the version number of the object, or ignore the object otherwise
ReplicationMode.OVERWRITE
Usecases for this feature include reconciling data entered into different database instances, upgrading system configuration information during product upgrades, rolling back changes made during non-ACID transactions and more.
The SQL statements are issued in the following order 1. 2. 3. 4. 5. 6. all entity insertions, in the same order the corresponding objects were saved using Session.save() all entity updates all collection deletions all collection element deletions, updates and insertions all collection insertions all entity deletions, in the same order the corresponding objects were deleted using Session.delete()
(An exception is that objects using native ID generation are inserted when they are saved.) Except when you explicity flush(), there are absolutely no guarantees about when the Session executes the JDBC calls, only the order in which they are executed. However, Hibernate does guarantee that the Query.list(..) will never return stale data; nor will they return the wrong data. 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 (and only 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 Seo 11.3.2, Sesso estendida e versionamento automtico).
sess = sf.openSession(); Transaction tx = sess.beginTransaction(); sess.setFlushMode(FlushMode.COMMIT); // allow queries to return stale state Cat izi = (Cat) sess.load(Cat.class, id); izi.setName(iznizi); // might return stale data sess.find("from Cat as cat left outer join cat.kittens kitten"); // change to izi is not flushed! ... tx.commit(); // flush occurs sess.close();
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 Captulo 11, Transaes e Concorrncia.
For each basic operation of the Hibernate session - including persist(), merge(), saveOrUpdate(), delete(), lock(), refresh(), evict(), replicate() - there is a corresponding cascade style. Respectively, the cascade styles are named create, merge, save-update, delete, lock, refresh, evict, replicate. If you want an operation to be cascaded along an association, you must indicate that in the mapping document. For example:
<one-to-one name="person" cascade="persist"/>
You may even use cascade="all" to specify that all operations should be cascaded along the association. The default cascade="none" specifies that no operations are to be cascaded. 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. Recommendations: It doesn't usually make sense to enable cascade on a <many-to-one> or <many-to-many> association. Cascade is often useful for <one-to-one> and <one-to-many> associations. If the child object's lifespan is bounded by the lifespan of the of the parent object make it a lifecycle object by specifying cascade="all,delete-orphan". Otherwise, you might not need cascade at all. But if you think that you will often be working with the parent and children together in the same transaction, and you want to save yourself some typing, consider using cascade="persist,merge,save-update".
Mapping an association (either a single valued association, or a collection) with cascade="all" marks the association as a parent/child style relationship where save/update/delete of the parent results in save/update/delete of the child or children. Futhermore, 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: If a parent is passed to persist(), all children are passed to persist() If a parent is passed to merge(), all children are passed to merge() If a parent is passed to save(), update() or saveOrUpdate(), all children are passed to saveOrUpdate() If a transient or detached child becomes referenced by a persistent parent, it is passed to saveOrUpdate() If a parent is deleted, all children are passed to delete() If a child is dereferenced by a persistent parent, nothing special happens - the application should explicitly delete the child if necessary - unless cascade="delete-orphan", in which case the "orphaned" child is deleted.
Finally, note that cascading of operations can be applied to an object graph at call time or at flush time. All operations, if enabled, are cascaded to associated entities reachable when the operation is executed. However, save-upate and delete-orphan are transitive for all associated entities reachable during flush of the Session.
tempos em tempos, este modelo muito til prpria aplicao. Por exemplo, a aplicao pode usar o metadados do Hibernate que executa um algoritmo "inteligente" que compreende quais objetos podem ser copiados (por exemplo, tipos de valores mutveis) ou no (por exemplo, tipos de valores imutveis e, possivelmente, entidades associadas). O Hibernate expe o metadados via interfaces ClassMetadata e CollectionMetadata e pela hierarquia Type. Instncias das interfaces de metadados podem ser obtidas a partir do SessionFactory.
Cat fritz = ......; ClassMetadata catMeta = sessionfactory.getClassMetadata(Cat.class); Object[] propertyValues = catMeta.getPropertyValues(fritz); String[] propertyNames = catMeta.getPropertyNames(); Type[] propertyTypes = catMeta.getPropertyTypes(); // get a Map of all properties which are not collections or associations Map namedValues = new HashMap(); for ( int i=0; i<propertyNames.length; i++ ) { if ( !propertyTypes[i].isEntityType() && !propertyTypes[i].isCollectionType() ) { namedValues.put( propertyNames[i], propertyValues[i] ); } }
127
Transaes e Concorrncia melhor do que uma unidade claramente definida do trabalho. A ltima opo tambm muito mais manutenvel e extensvel. O pattern mais comum em uma aplicao multi-usurio cliente/servidor sesso-por-requisio. Neste modelo, uma requisio do cliente enviada ao servidor (onde a camada de persistncia do Hibernate roda), uma Session nova do Hibernate aberta, e todas as operaes da base de dados so executadas nesta unidade do trabalho. Logo que o trabalho for completado (e a resposta para o cliente for preparada), a sesso descarregad e fechada. Voc usaria tambm uma nica transao de base de dados para servir s requisies dos clientes, comeando e commitando-o quando voc abre e fecha a Session. O relacionamento entre os dois um-para-um e este modelo um ajuste perfeito para muitas aplicaes. O desafio encontra-se na implementao. O Hibernate fornece gerncia integrada da "sesso atual" para simplificar este pattern. Tudo que voc tem que fazer iniciar uma transao quando uma requisio tem que ser processada e termina a transao antes que a resposta seja enviada ao cliente. Voc pode fazer onde quiser, solues comuns so ServletFilter, interceptador AOP com um pointcut (ponto de corte) nos mtodos de servio ou em um container de proxy/interceptao. Um container de EJB uma maneira padronizada para implementar aspectos cross-cutting tais como a demarcao da transao em EJB session beans, declarativamente com CMT. Se voc se decidir usar demarcao programtica de transao, de preferencia a API Transaction do Hibernate mostrada mais adiante neste captulo, para fcilidade no uso e portabilidade de cdigo. Seu cdigo de aplicao pode acessar a "sesso atual" para processar a requisio fazendo uma chamada simples a sessionFactory.getCurrentSession() em qualquer lugar e com a frequencia necessria. Voc sempre conseguir uma Session limitada a transao atual. Isto tem que ser configurado para recurso local ou os ambientes JTA. Veja Seo 2.5, Sesses contextuais. s vezes conveniente estender o escopo de uma Session e de uma transao do banco de dados at que a "viso esteja renderizada". especialmente til em aplicaes servlet que utilizam uma fase de rendenderizao separada depois que a requisio ter sido processada. Estendendo a transao at que renderizao da viso esteja completa fcil de fazer se voc implementar seu prprio interceptador. Entretanto, no se pode fazer facilmente se voc confiar em EJBs com transaes gerenciadas por continer, porque uma transao ser terminada quando um mtodo de EJB retornar, antes da renderizao de toda viso puder comear. Veja o website e o frum do Hibernate para dicas e exemplos em torno deste pattern Open Session in View.
Ns chamamos esta unidade de trabalho, do ponto da viso do usurio, executando uma longa conversao (ou transao da aplicao). H muitas maneiras de voc pode implementar em sua aplicao. Uma primeira implementao simples pode manter aSession e a transao aberta durante o tempo de interao do usurio, com bloqueios na base de dados para impedir a modificao concorrente e para garantir o isolamento e a atomicidade. Esse naturalmente um anti-pattern, desde que a disputa do bloqueio no permitiria o escalonameneto da aplicao com o nmero de usurios concorrentes.
129
Transaes e Concorrncia
Claramente, ns temos que usar diversas transaes para implementar a conversao. Neste caso, Manter o isolamento dos processos de negcio torna-se responsabilidade parcial da camada da aplicao. Uma nica conversao geralmente usa diversas transaes. Ela ser atmica se somente uma destas transaes (a ltima) armazenar os dados atualizados, todas as outras simplesmente leram os dados (por exemplo em um dilogo do estilo wizard que mede diversos ciclos de requisio/resposta). Isto mais fcil de implementar do que pode parecer, especialmente se voc usar as caractersticas do Hibernate: Versionamento automtico - O Hibernate pode fazer o controle automtico de concorrncia otimista para voc, ele pode automaticamente detectar se uma modificao concorrente ocorreu durante o tempo de interao do usurio. Geralmente ns verificamos somente no fim da conversao. Detached Objects- se voc se decidir usar o j discutido pattern session-per-request, todas as instncias carregadas estaro no estado destacado durante o tempo em que o usurio estiver pensando. O Hibernate permite que voc reatache os objetos e persita as modificaes, esse pattern chamado sessionper-request-with-detached-objects. usado versionamento automatico para isolar as modificaes concorrentes. Extended (or Long) Session A Session do Hibernate pode ser desligada da conexo bsica do JDBC depois que a transao foi commitada e ser reconectado quando uma nova requisio do cliente ocorrer. Este pattern conhecido como session-per-conversation e faz o reatamento uniforme desnecessrio. Versionamento automtico usado para isolar modificaes concorrentes e a session-per-conversation usualmente no permitido para ser nivelado automaticamente, e sim explicitamente.
Ambos session-per-request-with-detached-objects e session-per-conversation possuem vantagens e desvantagens, nos discutiremos mais tarde neste captulo no contexto do controle de concorrncia otimista.
Identidade da JVM
foo==bar
Ento para os objetos acoplados a um Session em particular (isto no escopo de um Session), as duas noes so equivalentes e a identidade da JVM para a identidade da base de dados garantida pelo Hibernate. Entretanto, quando a aplicao pode acessar concorrentemente o "mesmo" objeto do negcio (identidade persistente) em duas sesses diferentes, as duas instncias sero realmente "diferentes" (identidade de JVM). Os conflitos so resolvidos usando (versionamento automtico) no flush/commit, usando abordagem otimista. Este caminho deixa o Hibernate e o banco dedados se preocuparem com a concorrncia; tambm fornece uma escalabilidade melhor, garantindo que a identidade em unidades de trabalho nico-encadeadas no necessite de bloqueio dispendioso ou de outros meios de sincronizao. A aplicao nunca necessita sincronizar qualquer objeto de negcio to longo que transpasse uma nica thread por Session. Dentro de uma Session a aplicao pode usar com segurana o == para comparar objetos. Com tudo, uma aplicao que usa == fora de uma Session, pode ver resultados inesperados. Isto pode ocorrer mesmo em alguns lugares inesperados, por exemplo, se voc colocar duas instncias desacopladas em um mesHibernate 3.2 cr2 130
Transaes e Concorrncia mo Set. Ambos podem ter a mesma identidade na base de dados (isto eles representam a mesma linha em uma tabela), mas a identidade da JVM pela definio no garantida para instncias em estado desacoplado. O desenvolvedor tem que sobrescrever os mtodos equals() e hashCode() em classes persistentes e implementar sua prpria noo da igualdade do objeto. Advertncia: nunca use o identificador da base de dados para implementar a igualdade, use atributos de negcio, uma combinao nica, geralmente imutvel. O identificador da base de dados mudar se um objeto transiente passar para o estado persistente. Se a instncia transiente (geralmente junto com instncias desacopladas) for inserida em um Set, mudar o hashcode quebra o contrato do Set. Atributos para chaves de negcio no tm que ser to estvel quanto s chaves primrias 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 discusso mais completa sobre o assunto. Note tambm que esta no uma caracteristica do Hibernate, mas simplesmente como a identidade e a igualdade do objeto de Java tm que ser implementadas.
Transaes e Concorrncia
simples ou Swing) e ambientes gerenciados J2EE. Em um ambiente no gerenciado, o Hibernate geralmente responsvel pelo seu prprio pool de conexes. O desenvolvedor tem que manualmente ajustar limites das transaos, ou seja, comear, commitar, ou dar rollback nas transaes ele mesmo. Um ambiente gerenciado fornece transaes gerenciadas por continer (CMT - container-managed transactions), com um conjunto da transaes definido declarativamente em descritores de deployment de EJB session beans, por exemplo. A demarcao programtica ento j no necessrio. Entretanto, freqentemente desejvel manter sua camada de persistncia portvel entre ambientes de recurso locais no gerenciados e sistemas que podem confiar em JTA, mas usar BMT em vez de CMT. Em ambos os casos voc usaria demarcao de transao programtica. O Hibernate oferece uma API chamada Transaction que traduz dentro do sistema de transao nativa de seu ambiente de deployment. Esta API realmente opcional, mas ns encorajamos fortemente seu uso a menos que voc estiver em um CMT session bean. Geralmente, finalizar um Sessionenvolve quatro fases distintas: flush da sesso commitar a transao fechar a sesso tratar as excees
A limpeza da sesso j foi bem discutida, agora ns daremos uma olhada na demarcao da transao e na manipulao de exceo em ambientes controlados e no controlados.
Voc no pode chamar flush() do Session() explicitamente - a chamada ao commit() dispara automaticamente a sincronizao para a sesso (dependendo do Seo 10.10, Flushing the Session). Uma chamada ao close() marca o fim de uma sesso. A principal implicao do close() que a conexo JDBC ser abandonada pela sesso. Este cdigo Java portvel e funciona em ambientes no gerenciado e de JTA. Uma soluo muito mais flexvel gerncia integrada de contexto da "sesso atual" do Hibernate, como descrito anteriormente:
// Non-managed environment idiom with getCurrentSession()
132
Transaes e Concorrncia
try { factory.getCurrentSession().beginTransaction(); // 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 cdigo em uma aplicao regular; as excees fatais (do sistema) devem sempre ser pegas no "alto". Ou seja, o cdigo que executa chamadas do Hibernate (na camada de persistncia) e o cdigo que trata RuntimeException (e geralmente pode somente limpar acima e na sada) esto 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 manipulao de exceo discutida mais tarde neste captulo. Note que voc deve selecionar org.hibernate.transaction.JDBCTransactionFactory (que o padro) e para o segundo exemplo "thread" como seu hibernate.current_session_context_class.
Se voc quiser usar um Session limitada por transao, isto , a funcionalidade do getCurrentSession() para a propagao fcil do contexto, voc ter que usar diretamente a API JTA UserTransaction:
// BMT idiom with getCurrentSession() try { UserTransaction tx = (UserTransaction)new InitialContext() .lookup("java:comp/UserTransaction");
133
Transaes e Concorrncia
tx.begin(); // Do some work on Session bound to transaction factory.getCurrentSession().load(...); factory.getCurrentSession().persist(...); tx.commit(); } catch (RuntimeException e) { tx.rollback(); throw e; // or display error message }
Com CMT, a demarcao da transao feita em descritores de deployment do session beans, no programaticamente, conseqentemente, o cdigo reduzido a:
// CMT idiom Session sess = factory.getCurrentSession(); // do some work ...
Em um CMT/EJB mesmo um rollback acontece automaticamente, desde que uma exeo RuntimeException no tratvel seja lanada por um mtodo de um session bean que informa ao continer ajustar a transao global ao rollback. Isto significa que voc no necessita usar a API Transaction do Hibernate em tudo com BMT ou CMT e voc obtm a propagao automtica do Session "atual" limitada transao. Veja que voc dever escolher org.hibernate.transaction.JTATransactionFactory se voc usar o JTA diretamente (BMT) e org.hibernate.transaction.CMTTransactionFactory em um CMT session bean, quando voc configura a fbrica de transao do Hibernate. Lembre-se tambm de configurar o hibernate.transaction.manager_lookup_class. Alm disso, certifique-se que seu hibernate.current_session_context_class ou no configurado (compatibilidade com o legado) ou definido para "jta". A operao getCurrentSession() tem um aspecto negativo em um ambiente JTA. H uma advertncia para o uso do mtodo liberado de conexo after_statement, o qual usado ento por padro. Devido a uma limitao simples da especificao JTA, no possvel para o Hibernate automaticamente limpar quaisquer instncias ScrollableResults ou Iterator abertas retornadas pelo scroll() ou iterate(). Voc deve liberar o cursor subjacente da base de dados chamando ScrollableResults.close() ou Hibernate.close(Iterator) explicitamente de um bloco finally. (Claro que a maioria de aplicaes podem facilmente evitar o uso do scroll() ou do iterate() em todo cdigo provindo do JTA ou do CMT.)
Transaes e Concorrncia ao apropriada deve ser tomada. O Hibernate envolve SQLExceptions lanadas ao interagir com a base de dados em um JDBCException. Na realidade, o Hibernate tentar converter a exceo em em uma sub classe mais significativa da JDBCException. A SQLException subjacente est sempre disponvel atravs de JDBCException.getCause(). O Hibernate converte a SQLException em uma sub classe JDBCException apropriada usando SQLExceptionConverter associado ao SessionFactory. Por padro, o SQLExceptionConverter definido pelo dialeto configurado; entretanto, tambm possvel conectar em uma implementao customizada (veja o javadoc para mais detalhes da classe SQLExceptionConverterFactory). Os subtipos padro de JDBCException so: - indica um erro com a comunicao subjacente de JDBC. - indica um problema da gramtica ou da sintaxe com o SQL emitido. ConstraintViolationException - indica algum forma de violao de confinamento de integridade. LockAcquisitionException - indica um erro ao adquirir um nvel de bloqueio necessrio para realizar a operao de requisio. GenericJDBCException - uma exceo genrica que no cai em algumas das outras categorias.
JDBCConnectionException SQLGrammarException
Session sess = factory.openSession(); try { //set transaction timeout to 3 seconds sess.getTransaction().setTimeout(3); sess.getTransaction().begin(); // do some work ... sess.getTransaction().commit() } catch (RuntimeException e) { sess.getTransaction().rollback(); throw e; // or display error message } finally { sess.close(); }
Veja que setTimeout() no pode ser chamado em um CMT bean, onde os timeouts das transaes devem ser definidos declarativamente.
Transaes e Concorrncia
ver aplicaes que usam concorrncia otimista. Os casos de uso que ns mostramos esto no contexto de conversaes longas, mas a checagem de verso tambm tem o benefcio de impedir atualizaes perdidas em nicas transaes.
A propriedade version mapeada usando <version>, e o Hibernate vai increment-lo- automaticamente durante o flush se a entidade estiver alterada. Claro, se voc se estiver operando em um ambiente de baixa concorrncia de dados e no requerer a checagem de verso, voc pode usar este caminho e apenas saltar a checagem de verso. Nesse caso, o ultimo commit realizdo a estratgia padro para suas conversaes longas. Mantenha em mente que isto pode confundir os usurios da aplicao, assim como eles podem experimentar atualizaes perdidas sem mensagens de erro ou uma possibilidade ajustar mudanas de conflito. Claro que, checagem manual da verso somente praticvel em circunstncias triviais e no para a maioria de aplicaes. Freqentemente, os grafos completos de objetos modificados tm que ser verificados, no somente nicas instncias. O Hibernate oferece checagem de verso automtica com uma Session estendida ou instncias desatachadas como o paradigma do projeto.
136
Transaes e Concorrncia
Transaction t = session.beginTransaction(); // Obtain a new JDBC connection, start transaction foo.setProperty("bar"); session.flush(); t.commit(); session.close(); // Only for last transaction in conversation // Also return JDBC connection // Only for last transaction in conversation
O objeto foo sabe que Session j foi carregada. Comeando uma nova transao em uma sesso velha obtm uma conexo nova e recomea a sesso. Commitando uma transao desconecta uma sesso da conexo JDBC e retorna a conexo ao pool. Aps a reconexo, forar uma checagem de verso em dados que voc no est atualizando, voc pode chamar Session.lock() com o LockMode.READ em todos os objetos que possam ter sido atualizados por uma outra transao. Voc no necessita bloquear nenhum dado para atualizar. Geralmente voc configuraria FlushMode.NEVER em uma Session estendida, de modo que somente o ltimo ciclo da transao tenha permisso de persistir todas as modificaes feitas nesta conversao. Disso, somente esta ltima transao incluiria a operao flush() e ento chamar tambm close() da sesso para terminar a conversao. Este pattern problemtico se a Session for demasiadamente grande para ser armazenado durante o tempo que usurio pensar, por exemplo um HttpSession estiver mantido to pequeno quanto possvel. Como o Session tambm cache de primeiro nvel (imperativo) e contm todos os objetos carregados, ns podemos provavelmente usar esta estratgia somente para alguns ciclos de requisio/resposta. Voc deve usar a Session somente para uma nica conversao, porque ela logo tambm estar com dados velhos. (Note que verses mais atuais de Hibernate requerem a desconexo e o reconexo explcitas de uma Session. Estes mtodos so desatualizados, como o incio e trmino de uma transao tem o mesmo efeito.) Note tambm que voc deve manter a Session desconectada fechada para a camada de persistncia. Ou seja, use um EJB stateful session bean para prender a Session em um ambiente do trs camadas e no o transferir camada web (ou at serializ-lo para uma camada separada) para armazen-lo no HttpSession. O pattern sesso estendida, ou session-per-conversation, mais difcil de implementar com gerenciamento automtico de sesso atual. Voc precisa fornecer sua prpria implementao do CurrentSessionContext para isto (veja o Hibernate Wiki para exemplos).
Outra vez, o Hibernate verificar verses da instncia durante o flush, lanando uma exceo se ocorrer conflitos de atualizaes. Voc pode tambm chamar o lock() em vez de update() e usar LockMode.READ (executando uma checagem de verso, ignorando todos os caches) se voc estiver certo de que o objeto no foi modificado.
137
Transaes e Concorrncia
O lock obtido "explicitamente pelo usurio" se d em uma das seguintes maneiras: Uma chamada a Session.load(), especificando o LockMode. 138
Se uma Session.load() invocada com UPGRADE ou UPGRADE_NOWAIT, e o objeto requisitado ainda no foi carregado pela sesso, o objeto carregado usando SELECT ... FOR UPDATE. Se load() for chamado para um objeto que j foi carregado com um lock menos restritivo que o novo lock solicitado, o Hibernate invoca o mtodo lock() para aquele objeto. O mtodo Session.lock() executa uma verificao no nmero da verso se o modo de lock especificado for READ, UPGRADE ou UPGRADE_NOWAIT.. (No caso do UPGRADE ou UPGRADE_NOWAIT, usado SELECT ... FOR UPDATE.) Se o banco de dados no suportar o lock mode solicitado, o Hibernate vai usar um modo alternativo apropriado (ao invs de lanar uma exceo). Isso garante que a aplicao vai ser portvel.
O parmetro de configurao hibernate.connection.release_mode usado para especificar qual modo de liberao deve ser usado. Opes disponveis: retornado pelo mtodo Para JTATransactionFactory, ele retorna ConnectionReleaseMode.AFTER_STATEMENT; para JDBCTransactionFactory, ele retorna ConnectionReleaseMode.AFTER_TRANSACTION. Raramente uma boa idia alterar padro, como frequencia ao se fazer isso temos falhas que parecem bugs e/ou suposies invlidas no cdigo do usurio. on_close - indica o uso da ConnectionReleaseMode.ON_CLOSE. Essa opo foi deixada para manter a compatibilidade, mas seu uso fortemente desencorajado. after_transaction indica o uso da ConnectionReleaseMode.AFTER_TRANSACTION. Essa opo nada deve ser usada com ambientes JTA. Tambm note que no caso da ConnectionReleaseMode.AFTER_TRANSACTION, se a sesso foi colocada no modo auto-commit a conexo vai ser liberada de forma similar ao modo AFTER_STATEMENT. after_statement indica o uso ConnectionReleaseMode.AFTER_STATEMENT. Adicionalmente, o ConnectionProvider configurado consultado para verificar se suporta essa configurao ((supportsAggressiveRelease()). Se no suportar, o modo de liberao redefinido como ConnectionRelease-Moauto org.hibernate.transaction.TransactionFactory.getDefaultReleaseMode().
(padro)
essa
opo
delega
ao
modo
de
liberao
139
Transaes e Concorrncia de.AFTER_TRANSACTION. Essa configurao s segura em ambientes onde podemos readquirir a mesma conexo JDBC toda vez que o mtodo ConnectionProvider.getConnection() for chamado ou em um ambiente auto-commit onde no importa se ns recuperamos a mesma conexo.
140
12.1. Interceptadores
A interface Interceptor permite fornecer informaes da session para o aplicativo, permitindo ao aplicativo inspecionar e/ou manipular as propriedades de um objeto persistente antes de ser salvo, atualizado, excludo ou salvo. Um dos possveis usos gerar informaes de auditoria. Por exemplo, o seguinte Interceptor seta automaticamente o atributo createTimestamp quando um Auditable criada e atualiza o atributo lastUpdateTimestamp quando um Auditable atualizado. Voc pode implementar Auditable diretamente ou pode estender EmptyInterceptor, sendo que a segunda considerada a melhor opo.
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; public class AuditInterceptor extends EmptyInterceptor { private int updates; private int creates; private int loads; public void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { // do nothing } public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) { if ( entity instanceof Auditable ) { updates++; for ( int i=0; i < propertyNames.length; i++ ) { if ( "lastUpdateTimestamp".equals( propertyNames[i] ) ) { currentState[i] = new Date(); return true; } } } return false; } public boolean onLoad(Object entity, Serializable id, Object[] state,
141
Interceptadores e Eventos
String[] propertyNames, Type[] types) { if ( entity instanceof Auditable ) { loads++; } return false; } public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { if ( entity instanceof Auditable ) { creates++; for ( int i=0; i<propertyNames.length; i++ ) { if ( "createTimestamp".equals( propertyNames[i] ) ) { state[i] = new Date(); return true; } } } return false; } public void afterTransactionCompletion(Transaction tx) { if ( tx.wasCommitted() ) { System.out.println("Creations: " + creates + ", Updates: " + updates, "Loads: " + loads); } updates=0; creates=0; loads=0; } }
Os interceptadores podem ser aplicados em dois diferentes escopos: No escopo da Session e no escopo SessionFactory. Um interceptador no escopo da Session definido quando uma sesso aberta usando o mtodo sobrecarregado da SessionFactory.openSession() que aceita um Interceptor como parmetro.
Session session = sf.openSession( new AuditInterceptor() );
Um interceptador no escopo da SessionFactory definido no objeto Configuration antes da SessionFactory ser instanciada. Nesse caso, o interceptador fornecido ser aplicado para todas as sesses abertas por aquela SessionFactory; Isso apenas no ocorrer caso seja especificado um interceptador no momento em que a sesso for aberta. Um interceptador no escopo de SessionFactory deve ser thread safe, tomando-se o cuidado de no armazenar atributos de estado especficos da sesso, pois, provavelmente, mltiplas sesses iro utilizar esse interceptador simultaneamente.
new Configuration().setInterceptor( new AuditInterceptor() );
Interceptadores e Eventos
LoadEvent,
um LoadEvent, etc (consulte o DTD do XML de configurao ou o pacote org.hibernate.event para a lista completa dos tipos de eventos). Quando uma requisio feita em um desses mtodos, a Session do hibernate gera um evento apropriado e o envia para o listener de evento correspondente quele tipo de evento. Esses listeners implementam a mesma lgica que aqueles mtodos, trazendo os mesmos resultados. Entretanto, voc livre para implementar uma customizao de um desses listeners (isto , o LoadEvent processado pela implementao registrada da interface LoadEventListener), ento sua implementao vai ficar responsvel por processar qualquer requisio load() feita pela Session. Para todos os efeitos esses listeners deve ser considerados singletons; ou seja, eles so compartilhados entre as requisies, e assim sendo, no devem salvar nenhum estado das variveis instanciadas. Um listener personalizado deve implementar a interface referente ao evento a ser processado e/ou deve estender a classes base equivalente (ou mesmo os listeners padres usados pelo Hibernate, eles no so declarados como finais com esse objetivo). O listener personalizado pode ser registrado programaticamente no objeto Configuration, ou declarativamente no XML de configurao do Hibernate (o registro do listener no propertie de configurao no suportado). Aqui temos um exemplo de como carregar um listener personalizado:
public class MyLoadListener implements LoadEventListener { // this is the single method defined by the LoadEventListener interface public void onLoad(LoadEvent event, LoadEventListener.LoadType loadType) throws HibernateException { if ( !MySecurity.isAuthorized( event.getEntityClassName(), event.getEntityId() ) ) { throw MySecurityException("Unauthorized access"); } } }
Voc tambm precisa adicionar uma entrada no XML de configurao do Hibernate para registrar declarativamente qual listener deve se utilizado em conjunto com o listener padro:
<hibernate-configuration> <session-factory> ... <event type="load"> <listener class="com.eg.MyLoadListener"/> <listener class="org.hibernate.event.def.DefaultLoadEventListener"/> </event> </session-factory> </hibernate-configuration>
Listeners registrados declarativamente no compartilham da mesma instancia. Se o mesmo nome da classe utilizado em vrios elementos e <listener/>, cada um vai resultar em uma instancia separada dessa classe. Se voc tem a necessidade de compartilhar uma instancia de um listener entre diversos tipos de listeners voc deve registrar o listener programaticamente. Mas porque implementar uma interface e definir o tipo especfico durante a configurao? Bem, um listener pode implementar vrios listeners de evento. Com o tipo sendo definido durante o registro, fica fcil ligar ou desligar listeners personalizados durante a configurao.
143
Interceptadores e Eventos
Usually, declarative security in Hibernate applications is managed in a session facade layer. Now, Hibernate3 allows certain actions to be permissioned via JACC, and authorized via JAAS. This is optional functionality built on top of the event architecture. First, you must configure the appropriate event listeners, to enable the use of JAAS authorization.
<listener <listener <listener <listener type="pre-delete" class="org.hibernate.secure.JACCPreDeleteEventListener"/> type="pre-update" class="org.hibernate.secure.JACCPreUpdateEventListener"/> type="pre-insert" class="org.hibernate.secure.JACCPreInsertEventListener"/> type="pre-load" class="org.hibernate.secure.JACCPreLoadEventListener"/>
Note that <listener type="..." class="..."/> is just a shorthand for <event type="..."><listener class="..."/></event> when there is exactly one listener for a particular event type. Next, still in hibernate.cfg.xml, bind the permissions to roles:
<grant role="admin" entity-name="User" actions="insert,update,read"/> <grant role="su" entity-name="User" actions="*"/>
The role names are the roles understood by your JACC provider.
144
Isto ir falhar em algum lugar prximo a linha 50.000, lanando uma OutOfMemoryException. Isso ocorre devido ao fato do Hibernate fazer cache de todas as instncias de Customer inseridas num cach em nvel de sesso. Neste captulo veremos como contornar esse problema. Entretanto, se voc vai realizar processamento de lotes, muito importante que voc habilite o uso de lotes JDBC, se voc pretende obter um desempenho razovel. Defina o tamanho do lote JDBC em um valor razovel (algo entre 10-50):
hibernate.jdbc.batch_size 20
Voc tambm pode querer rodar esse tipo de processamento de lotes com o cache secundrio completamente desabilitado:
hibernate.cache.use_second_level_cache false
Mas isto no absolutamente necessrio, desde que ns possamos ajustar o CacheMode para desabilitar a interao com o cache secundrio.
145
Processamento de lotes
Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); ScrollableResults customers = session.getNamedQuery("GetCustomers") .setCacheMode(CacheMode.IGNORE) .scroll(ScrollMode.FORWARD_ONLY); int count=0; while ( customers.next() ) { Customer customer = (Customer) customers.get(0); customer.updateStuff(...); if ( ++count % 20 == 0 ) { //flush a batch of updates and release memory: session.flush(); session.clear(); } } tx.commit(); session.close();
Veja neste exempo, as instancias de Customer retornadas pela query so imediatamente desvinculadas. Elas nunca sero assossiadas um contexto persistente. As operaes insert(), update() e delete() definidos pela interface StatelessSession so considerados operaes diretas no banco de dados (row-level operations), isso resulta em uma execuo imediata de comandos SQL INSERT, UPDATE ou DELETE respectivamente. Devido a isso, eles possuem uma semntica bem diferente das operaes save(), saveOrUpdate() ou delete() definidas na interface Session.
Processamento de lotes
ses SQL Data Manipulation Language (SQL-style DML): INSERT, UPDATE, DELETE) os dados diretamente no banco de dados no ir afetar o estado registrado em memria. Entretanto, o Hibernate prov mtodos para executar queries SQL-style DML, que so totalmente executas com HQL (Hibernate Query Language) (Captulo 14, HQL: A linguagem de Queries do Hibernate). A pseudo-sintaxe para expresses UPDATE e DELETE : ( UPDATE | DELETE ) FROM? NomeEntidade (WHERE condies_where)?. Algumas observaes: Na clausula from, a palavra chave FROM opcional; Somente uma entidade pode ser chamada na clausula from; opcionalmente pode ter um alias. Se o nome da entidade for possuir um alias, ento qualquer propriedade referenciada deve usar esse alias qualificado; se o nome da entidade no possuir um alias, ento nenhuma das propriedade precisa usar o acesso qualificado. Na Seo 14.4, Formas e sintaxe de joins (ambas implcita ou explicita) pode ser especificada em um bulk HQL query. Sub-queries podem ser usadas na clausula where; as subqueries podem conter joins. A clausula where tambm opcional.
Como exemplo para executar um HQL UPDATE, use o mtodo Query.executeUpdate()(o mtodo ganhou o nome devido a sua familiaridade com o do JDBC PreparedStatement.executeUpdate()):
Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); 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();
HQL UPDATE statements, by default do not effect the Seo 5.1.7, version (optional) or the Seo 5.1.8, timestamp (optional) property values for the affected entities; this is in keeping with the EJB3 specification. However, you can force Hibernate to properly reset the version or timestamp property values through the use of a versioned update. This is achieved by adding the VERSIONED keyword after the UPDATE keyword.
Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); String hqlVersionedUpdate = "update versioned Customer set name = :newName where name = :oldName"; int updatedEntities = s.createQuery( hqlUpdate ) .setString( "newName", newName ) .setString( "oldName", oldName ) .executeUpdate(); tx.commit(); session.close();
Note that custom version types (org.hibernate.usertype.UserVersionType) are not allowed in conjunction with a update versioned statement. Para executar um HQL DELETE, use o mesmo mtodo Query.executeUpdate():
Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); String hqlDelete = "delete Customer c where c.name = :oldName"; // or String hqlDelete = "delete Customer where name = :oldName"; int deletedEntities = s.createQuery( hqlDelete ) .setString( "oldName", oldName ) .executeUpdate(); tx.commit(); session.close();
147
Processamento de lotes
O valor int retornado pelo mtodo Query.executeUpdate() indica o numero de entidade afetadas pela operao. Lembre-se que isso pode estar ou no relacionado ao nmero de linhas alteradas no banco de dados. Uma operao bulk HQL pode resultar em vrias expresses SQL reais a serem executadas, por exemplo, no caso de joined-subclass. O nmero retornado indica a quantidade real de entidades afetadas pela expresso. Voltando ao exemplo da joined-subclass, a excluso de uma das subclasses pode resultar numa excluso em outra tabelas, no apenas na tabela para qual a subclasses est mapeada, mas tambm tabela "root" e possivelmente nas tabelas joined-subclass num nvel hierrquico imediatamente abaixo. A pseudo-sintaxe para o comando INSERT : INSERT INTO EntityName properties_list select_statement. Alguns pontos a observar: Apenas a forma INSERT INTO ... SELECT ... suportada; INSERT INTO ... VALUES ... no suportada. A lista de propriedade anloga especificao da coluna do comando SQL INSERT. Para entidades envolvidas em mapeamentos, apenas a propriedades definidas diretamente a nvel da classe podem ser usandas na properties_list. Propriedades da superclass no so permitidas; e as propriedades da subclasse no faz sentido. Em outras palavras, os comandos INSERT no so polimorficos. O camando select pode ser qualquer query HQL vlida, que tenha um retorno compatvel com o tipo com o esperado pela incluso. Atualmente, isto verificado durante a compilao da query, isto melhor do que permitir que a verificao chegue ao banco de dados. Entretanto perceba que isso pode causar problemas entre os Tipo do Hibernate que so equivalentes em oposio a equal. Isso pode causar problemas nas combinaes entre a propriedade definida como org.hibernate.type.DateTypee um propriedade definida como org.hibernate.type.TimestampType, embora o banco de dados no possa fazer uma distino ou possa ser capaz de manusear a converso. Para a propriedade id, a expresso insert oferece duas opes. Voc pode especificar qualquer propriedade id explicitamente no properties_list (em alguns casos esse valor obtido diretamente da expresso select) ou pode omitir do properties_list (nesse caso, um valor gerado usado). Essa ultima opo s vlida quando so usados geradores de ids que operam no banco de dados; a tentativa de usar essa opo com geradores do tipo "em memria" vai causar um exceo durante a etapa de parser. Veja a finalidades desta discusso, os seguintes geradores operam com o banco de dados org.hibernate.id.SequenceGenerator (e suas subclasses) e qualquer implementao de org.hibernate.id.PostInsertIdentifierGenerator. Aqui, a exceo mais notvel o org.hibernate.id.TableHiLoGenerator, que no pode ser usado porque ele no dispe de mecanismos para recuperar o seu valor. For properties mapped as either version or timestamp, the insert statement gives you two options. You can either specify the property in the properties_list (in which case its value is taken from the corresponding select expressions) or omit it from the properties_list (in which case the seed value defined by the org.hibernate.type.VersionType is used). Para propriedades mapeadas como version ou timestamp, a expresso insert oferece a voc duas opes. Voc pode especificar a propriedade na properties_list (nesse caso o seu valor obtido a partir da expresso select correspondente) ou ele pode ser omitido da properties_list (neste caso o usa o valor semente 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();
148
Ela ir retornar todas as instancias da classe eg.Cat. Necessariamente no precisamos qualificar o nome da classe, pois realizado auto-import por padro. Por isso na maior parte do tempos ns simplesmente escrevemos:
from Cat
Na maior parte do tempo, voc precisar atribuir um alias, desde que voc queira se referia ao Cat em outras partes da query.
from Cat as cat
Essa query atribui um alias a cat para as instancias de Cat, ento ns podemos usar esse alias depois na query. A palavra chave as opcional; poderamos escrever assim:
from Cat cat
Mltiplas classes pode ser envolvidas, resultando em um produto cartesiano ou "cross" join.
from Formula, Parameter
considerada uma boa prtica os nomes dos aliases comearem com letra minscula, aderente com os padres Java para variveis locais (ex: domesticCat).
Ns tambm podemos querer atribuir aliases em uma entidade associada, ou mesmo em elementos de uma coleo de valores, usando um join.
from Cat as cat inner join cat.mate as mate left outer join cat.kittens as kitten
(geralmente no til)
The inner join, left outer join and right outer join constructs may be abbreviated. As construes inner join, left outer join e right outer join podem ser abreviadas.
from Cat as cat join cat.mate as mate left join cat.kittens as kitten
Voc pode fornecer condies extras de join usando a palavra chave do HQL with.
from Cat as cat left join cat.kittens as kitten <literal>with</literal> kitten.bodyWeight > 10.0
Adicionalmente, um "fetch" join permite que associaes ou colees de valores sejam inicializadas junto com o objeto pai, usando apenas um select. Isso muito til no caso das colees. Isso efetivamente sobre escreve as declaraes outer join e lazy do arquivo mapeamento para associaes e colees. Veja a seo Seo 19.1, Estratgias de Fetching para mais informaes.
from Cat as cat inner join <literal>fetch</literal>cat.mate left join <literal>fetch</literal>cat.kittens
Usualmente, um fetchjoin no precisa atribuir um alias, pois o objeto associado no deve ser usado na clausula where (ou em qualquer outra clausula). Tambm, os objetos associados no so retornados diretamente nos resultados da query. Ao invs disso, eles devem ser acessados usando o objeto pai. A nica razo que ns podemos necessitar de um alias quando fazemos um fech join recursivamente em uma coleo adicional:
from Cat as cat inner join <literal>fetch</literal>cat.mate left join <literal>fetch</literal>cat.kittens child left join <literal>fetch</literal>child.kittens
Observe que a construo fetch no deve ser usada em queries invocadas usando iterate() (embora possa ser usado com scroll()). O fetch tambm no deve ser usado junto com o setMaxResults() ou setFirstResult() pois essas operaes so baseadas nas linhas retornadas, que normalmente contem duplicidade devido ao fetching das colees, ento o nmero de linhas pode no ser o que voc espera. O fetch no deve ser usado junto com uma condio with em uma condio with ad hoc. possvel que seja criado um produto cartesiano pelo join fetching em mais do que uma coleo em uma query, ento tome cuidado nesses casos. Um join fetHibernate 3.2 cr2 150
HQL: A linguagem de Queries do Hibernate ching em varias colees pode trazer resultados inesperados para mapeamentos do tipo bag, tome cuidado na hora de formular queries como essas. Finalmente, observe o seguinte, o full join fetch e right join fetch no so significativos. Se est usando o nvel de propriedade lazy (com instrumentao de bytecode), possvel forar o Hibernate a buscar as propriedades lazy imediatamente (na primeira query), usando fetch all properties .
from Document <literal>fetch</literal>all properties order by name
A query selecionar mates (companheiros), de outros Cats. Atualmente, podemos expressar a query de forma mais compacta como:
select cat.mate from Cat cat
Queries podem retornar propriedades de qualquer tipo de valor, incluindo propriedades de tipo de componente:
select cat.name from DomesticCat cat where cat.name like 'fri%'
Queries podem retornar mltiplos objetos e/ou propriedades como um array do tipo Object[],
select mother, offspr, mate.name from DomesticCat as mother inner join mother.mate as mate left outer join mother.kittens as offspr
ou como um List,
select new list(mother, offspr, mate.name)
151
from DomesticCat as mother inner join mother.mate as mate left outer join mother.kittens as offspr
assumindo que a classe Family tenha um construtor apropriado. Pode-se designar referencias a expresses selecionadas, as:
select max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n from Cat cat
Isto bem mais til quando usado junto com select new map:
select new map( max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n ) from Cat cat
Pode-se usar operadores aritimticos, concatenao e funes SQL reconhecidas na clausula select:
select cat.weight + sum(kitten.weight) from Cat cat join cat.kittens kitten group by cat.id, cat.weight
As palavras distinct e all podem ser usadas e tm a mesma semntica como no SQL.
select distinct cat.name from Cat cat select count(distinct cat.name), count(cat) from Cat cat
A query:
from Cat as cat
retorna instancias no s de Cat, mas tambm de subclasses como DomesticCat. As queries do Hibernate podem nomear qualquer classe Java ou interface na clausula from. A query retornar instancias de toda classe persistente que extenda a determinada classe ou implemente a determinada interface. A query , a seguir, pode retornar todo objeto persistente:
from java.lang.Object o
Note que as duas ltimas queries requerem mais de um SQL SELECT . Isto significa que a clausula order by no ordena corretamente todo o resultado. (Isso tambm significa que voc no pode chamar essas queries usando Query.scroll().)
retornar todas as instancias de Foo, para cada um que tiver uma instancia de bar com a propriedade date igual a propriedade startDate de Foo. Expresses de filtro compostas fazem da clausula where, extremamente poderosa. Consideremos:
from Cat cat where cat.mate.name is not null
Esta query traduzida para uma query SQL com uma tabela (inner) join. Se fosse escrever algo como:
from Foo foo where foo.bar.baz.customer.address.city is not null
Poderia-se terminar com uma query que necessitasse de join de quatro tabelas, no SQL. O operador = pode ser uasdo para comparar no apenas propriedades, mas tambm instancias:
from Cat cat, Cat rival where cat.mate = rival.mate
153
A propriedade especial (lowercase) id pode ser usada para referenciar o identificador nico de um objeto. (Pode-se usar tambm o nome de sua propriedade)
from Cat as cat where cat.id = 123 from Cat as cat where cat.mate.id = 69
A Segunda query eficiente. Nenhuma unio de tabelas necessria! As propriedades de identificadores compostas tambm podem ser usadas. Suponha que Person tenha um identificador composto que consiste de country e medicareNumber.
from bank.Person person where person.id.country = 'AU' and person.id.medicareNumber = 123456
Mais uma vez, a Segunda query no precisa de nenhum join de tabela. Assim mesmo, a propriedade especial class acessa o valor discriminador da instancia, no caso de persistncia polimrfica. O nome de uma classe Java inclusa em uma clausula "where", ser traduzida para seu valor descriminante.
from Cat cat where cat.class = DomesticCat
Pode-se tambm especificar as propriedades dos components ou tipos de usurio composto (e de componentes de componentes). Nunca tente usar uma expresso de filtro que termine na propriedade de um tipo de componente (ao contrrio de uma propriedade de um componente). Por exemplo, se store.owner uma entidade com um componente address.
store.owner.address.city store.owner.address // okay // error!
Um tipo "any" tem as propriedades id e class especiais, ns permitindo expressar um join da seguinte forma (onde AuditLog.item uma propriedade mapeada com <any>)
from AuditLog log, Payment payment where log.item.class = 'Payment' and log.item.id = payment.id
Veja que log.item.class e payment.class podem referir-se a valores de colunas de banco de dados completamente diferentes, na query acima.
14.9. Expresses
As expresses permitidas na clusula where inclui a maioria das coisas que voc poderia escrever no SQL: operadores matemticos +, -, *, / operadores de comparao binrios =, >=, <=, <>, !=, like operadores lgicos and, or, not 154
and not member of case "simples" , case ... when ... then ... else ... end, and "searched" case, case when ... then concatenao de string ...||... ou concat(...,...)
qualquer funcao ou operador definida pela EJB-QL 3.0: substring(), trim(), lower(), upper(),
length(), locate(), abs(), sqrt(), bit_length(), mod()
in
and nullif() para converter valores numericos ou temporais para string cast(... as ...), onde o segundo argumento o nome do tipo hibernate, eextract(... from ...) se ANSI cast() e extract() suportado pele banco de dados usado A funo HQL index() , que se aplicam a referencias de colees associadas e indexadas As funes hql que retornam expresses de colees de valores: size(), minelement(), maxelement(), minindex(), maxindex(), junto com o elemento especial, elements(), e funes de ndice que podem ser quantificadas usando some, all, exists, any, in. Qualquer funo escalar pelo bando de dados como sign(), trunc(), rtrim(), sin() Parametros posicionais ao estilo JDBC ? Parametros nomeados :name, :start_date, :x1 Literais SQL 'foo', 69, 6.66E+2, '1970-01-01 10:00:01.0' Constantes Java public static final ex: Color.TABBY
coalesce() str()
Likewise, is null and is not null may be used to test for null values. Assim mesmo, , is null e is not null podem ser usados para testar valores nulos. Booleanos podem ser facilmente usados em expresses, declarando as substituies da HQL query, na configurao do Hibernate
<property name="hibernate.query.substitutions">true 1, false 0</property>
Isso ir substituir as palavras chave true e false pelos literais 1 e 0 na traduo do HQL para SQL.
from Cat cat where cat.alive = true
Pode-se testar o tamanho de uma coleo com a propriedade especial size, ou a funo especial size().
from Cat cat where cat.kittens.size > 0
Para colees indexadas, voc pode se referir aos ndices mximo e mnimo, usando as funes minindex e maHibernate 3.2 cr2 155
Similarmente, pode-se referir aos elementos mximo e mnimo de uma coleo de tipos bsicos usando as funes minelement e maxelement.
from Calendar cal where maxelement(cal.holidays) > current_date
As funes SQL any, some, all, exists, in so suportadas quando passado o elemento ou o conjunto de ndices de uma coleo (elements e indices de funes), ou o resultado de uma subquery (veja abaixo).
select mother from Cat as mother, Cat as kit where kit in elements(foo.kittens)
Note que essas construes - size, elements, indices, minindex, maxindex, minelement, maxelement s podem ser usados na clausula where do Hibernate3. Elementos de colees com ndice (arrays, lists, maps), podem ser referenciadas pelo ndice (apenas na clausula where):
from Order order where order.items[0].id = 1234
select person from Person person, Calendar calendar where calendar.holidays['national day'] = person.birthDay and person.nationality.calendar = calendar
select item from Item item, Order order where order.items[ order.deliveredItemIndices[0] ] = item and order.id = 11
select item from Item item, Order order where order.items[ maxindex(order.items) ] = item and order.id = 11
O HQL tambm prov a funo interna index(), para elementos de associao um-pra-muitos ou coleo de valores.
select item, index(item) from Order order join order.items item where index(item) < 5
156
Se ainda ainda no est totalmente convencido, pense o quo maior e menos legvel poderia ser a query 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)
select foo.id, avg(name), max(name) from Foo foo join foo.names name group by foo.id
157
Funes SQL e funes agregadas so permitidas nas clausulas having e order by, se suportadas pelo banco de dados subjacente (ex: no no MySQL).
select cat from Cat cat join cat.kittens kitten group by cat having avg(kitten.weight) > 100 order by count(kitten) asc, sum(kitten.weight) desc
Note que, nem a clausula group by ou order by, podem conter expresses aritimticas.
14.12. Subqueries
Para bancos de dados que suportem subselects, o Hibernate suporta subqueries dentro de queries. Uma subquery precisa estar entre parnteses (normalmente uma chamada de funo agregada SQL). Mesmo subqueries co-relacionadas (subqueries que fazem referncia alias de outras queries), so aceitas.
from Cat as fatcat where fatcat.weight > ( select avg(cat.weight) from DomesticCat cat )
from DomesticCat as cat where cat.name = some ( select name.nickName from Name as name )
from Cat as cat where not exists ( from Cat as mate where mate.mate = cat )
from DomesticCat as cat where cat.name not in ( select name.nickName from Name as name )
select cat.id, (select max(kit.weight) from cat.kitten kit) from Cat as cat
Note que HQL subqueries podem aparecer apenas dentro de clausulas select ou where. Para subqueries com mais de uma expresso na lista do select, pode-se usar um construtor de tuplas:
from Cat as cat where not ( cat.name, cat.color ) in ( select cat.name, cat.color from DomesticCat cat )
Veja que em alguns bancos de dados (mas no o Oracle ou HSQL), pode-se usar construtores de tuplas em outros contextos. Por exemplo quando buscando componentes ou tipos de usurio composto.
from Person where name = ('Gavin', 'A', 'King')
158
H duas razes boas que voc pode no querer fazer este tipo de coisa: primeira, no completamente portvel entre plataformas de banco de dados; segunda, a query agora dependente da ordem de propriedades no documento de mapeamento.
Que monstro! Atualmente, na vida real, eu no sou muito afeioado a subqueries, ento minha query seria mais parecida com isto:
select order.id, sum(price.amount), count(item) from Order as order join order.lineItems as item join item.product as product, Catalog as catalog join catalog.prices as price where order.paid = false and order.customer = :customer and price.product = product and catalog = :currentCatalog group by order having sum(price.amount) > :minAmount order by sum(price.amount) desc
A prxima query conta o nmero de pagamentos em cada status, tirando todos os pagamentos com status AWAITING_APPROVAL, onde a mais recente mudana de status foi feita pelo usurio corrente. Traduz-se para uma query SQL com dois inner joins e um subselect correlacionado, nas tabelas PAYMENT, PAYMENT_STATUS e PAY-
159
select count(payment), status.name from Payment as payment join payment.currentStatus as status join payment.statusChanges as statusChange where payment.status.name <> PaymentStatus.AWAITING_APPROVAL or ( 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
Se eu tivesse mapeado a Collection statusChanges como um List, ao invs de um Set, a query teria sido muito mais simples de escrever.
select count(payment), status.name from Payment as payment join payment.currentStatus as status where payment.status.name <> PaymentStatus.AWAITING_APPROVAL or payment.statusChanges[ maxIndex(payment.statusChanges) ].user <> :currentUser group by status.name, status.sortOrder order by status.sortOrder
A prxima query usa a funo isNull() do MS SQL Server, para retornar todas as contas e pagamentos no pagos para a organizao, para cada usurio corrente pertencente. Traduz-se para uma query SQL com trs inner joins, um outer join e um subselect nas tabelas ACCOUNT, PAYMENT, PAYMENT_STATUS,ACCOUNT_TYPE, ORGANIZATION e ORG_USER .
select account, payment from Account as account left outer join account.payments as payment where :currentUser in elements(account.holder.users) and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID) order by account.type.sortOrder, account.accountNumber, payment.dueDate
160
Para ordenar um resultado pelo tamanho de uma Collection, use a query a seguir.
select usr.id, usr.name from User as usr left join usr.messages as msg group by usr.id, usr.name order by count(msg)
Se seu banco de dados suporta subselects, pode-se colocar uma condio sobre tamanho de seleo na clusula where da sua query:
from User usr where size(usr.messages) >= 1
Com essa soluo no se pode retornar um User com sem nenhuma menssagem, por causa do "inner join", a forma a seguir tambm til.
select usr.id, usr.name from User as usr left join usr.messages as msg group by usr.id, usr.name having count(msg) = 0
161
List cats = sess.createCriteria(Cat.class) .add( Restrictions.in( "name", new String[] { "Fritz", "Izi", "Pk" } ) ) .add( Restrictions.disjunction() .add( Restrictions.isNull("age") ) .add( Restrictions.eq("age", new Integer(0) ) ) .add( Restrictions.eq("age", new Integer(1) ) ) .add( Restrictions.eq("age", new Integer(2) ) ) ) ) .list();
Existe um grande nmero de critrios pr fabricados (subclasses de Restrictions), mas um especialmente til pois permite especificar o SQL diretamente.
List cats = sess.createCriteria(Cat.class) .add( Restrictions.sqlRestriction("lower({alias}.name) like lower(?)", "Fritz%", Hibernate.STRING) .list();
O parametro {alias} ser substituido pelo alias da entidade procurada. Uma maneira alternativa de obter um critrio peg-lo de uma instancia de Property. Voc pode criar uma Property chamando Property.forName().
162
Property age = Property.forName("age"); List cats = sess.createCriteria(Cat.class) .add( Restrictions.disjunction() .add( age.isNull() ) .add( age.eq( new Integer(0) ) ) .add( age.eq( new Integer(1) ) ) .add( age.eq( new Integer(2) ) ) ) ) .add( Property.forName("name").in( new String[] { "Fritz", "Izi", "Pk" } ) ) .list();
List cats = sess.createCriteria(Cat.class) .add( Property.forName("name").like("F%") ) .addOrder( Property.forName("name").asc() ) .addOrder( Property.forName("age").desc() ) .setMaxResults(50) .list();
15.4. Associations
You may easily specify constraints upon related entities by navigating associations using createCriteria().
List cats = sess.createCriteria(Cat.class) .add( Restrictions.like("name", "F%") ) .createCriteria("kittens") .add( Restrictions.like("name", "F%") ) .list();
note that the second createCriteria() returns a new instance of Criteria, which refers to the elements of the kittens collection. The following, alternate form is useful in certain circumstances.
List cats = sess.createCriteria(Cat.class) .createAlias("kittens", "kt") .createAlias("mate", "mt") .add( Restrictions.eqProperty("kt.name", "mt.name") ) .list();
(createAlias() does not create a new instance of Criteria.) Note that the kittens collections held by the Cat instances returned by the previous two queries are not prefiltered by the criteria! If you wish to retrieve just the kittens that match the criteria, you must use a ResultTransformer.
List cats = sess.createCriteria(Cat.class)
163
.createCriteria("kittens", "kt") .add( Restrictions.eq("name", "F%") ) .setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP) .list(); Iterator iter = cats.iterator(); while ( iter.hasNext() ) { Map map = (Map) iter.next(); Cat cat = (Cat) map.get(Criteria.ROOT_ALIAS); Cat kitten = (Cat) map.get("kt"); }
This query will fetch both mate and kittens by outer join. See Seo 19.1, Estratgias de Fetching for more information.
Version properties, identifiers and associations are ignored. By default, null valued properties are excluded. You can adjust how the Example is applied.
Example example = Example.create(cat) .excludeZeroes() //exclude zero valued properties .excludeProperty("color") //exclude the property named "color" .ignoreCase() //perform case insensitive string comparisons .enableLike(); //use like for string comparisons List results = session.createCriteria(Cat.class) .add(example) .list();
You can even use examples to place criteria upon associated objects.
List results = session.createCriteria(Cat.class) .add( Example.create(cat) ) .createCriteria("mate") .add( Example.create( cat.getMate() ) ) .list();
164
List results = session.createCriteria(Cat.class) .setProjection( Projections.projectionList() .add( Projections.rowCount() ) .add( Projections.avg("weight") ) .add( Projections.max("weight") ) .add( Projections.groupProperty("color") ) ) .list();
There is no explicit "group by" necessary in a criteria query. Certain projection types are defined to be grouping projections, which also appear in the SQL group by clause. An alias may optionally be assigned to a projection, so that the projected value may be referred to in restrictions or orderings. Here are two different ways to do this:
List results = session.createCriteria(Cat.class) .setProjection( Projections.alias( Projections.groupProperty("color"), "colr" ) ) .addOrder( Order.asc("colr") ) .list();
The alias() and as() methods simply wrap a projection instance in another, aliased, instance of Projection. As a shortcut, you can assign an alias when you add the projection to a projection list:
List results = session.createCriteria(Cat.class) .setProjection( Projections.projectionList() .add( Projections.rowCount(), "catCountByColor" ) .add( Projections.avg("weight"), "avgWeight" ) .add( Projections.max("weight"), "maxWeight" ) .add( Projections.groupProperty("color"), "color" ) ) .addOrder( Order.desc("catCountByColor") ) .addOrder( Order.desc("avgWeight") ) .list();
List results = session.createCriteria(Domestic.class, "cat") .createAlias("kittens", "kit") .setProjection( Projections.projectionList() .add( Projections.property("cat.name"), "catName" ) .add( Projections.property("kit.name"), "kitName" ) ) .addOrder( Order.asc("catName") ) .addOrder( Order.asc("kitName") ) .list();
165
List results = session.createCriteria(Cat.class) .setProjection( Projections.projectionList() .add( Projections.rowCount().as("catCountByColor") ) .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();
A DetachedCriteria may also be used to express a subquery. Criterion instances involving subqueries may be obtained via Subqueries or Property.
DetachedCriteria avgWeight = DetachedCriteria.forClass(Cat.class) .setProjection( Property.forName("weight").avg() ); session.createCriteria(Cat.class) .add( Property.forName("weight).gt(avgWeight) ) .list();
DetachedCriteria weights = DetachedCriteria.forClass(Cat.class) .setProjection( Property.forName("weight") ); session.createCriteria(Cat.class) .add( Subqueries.geAll("weight", weights) ) .list();
166
dation algorithm: lookups by a constant natural key. In some applications, this kind of query occurs frequently. The criteria API provides special provision for this use case. First, you should map the natural key of your entity using <natural-id>, and enable use of the second-level cache.
<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 that this functionality is not intended for use with entities with mutable natural keys. Next, enable the Hibernate query cache. Now, Restrictions.naturalId() allows us to make use of the more efficient cache algorithm.
session.createCriteria(User.class) .add( Restrictions.naturalId() .set("name", "gavin") .set("org", "hb") ).setCacheable(true) .uniqueResult();
167
These will both return a List of Object arrays (Object[]) with scalar values for each column in the CATS table. Hibernate will use ResultSetMetadata to deduce the actual order and types of the returned scalar values. To avoid the overhead of using ResultSetMetadata or simply to be more explicit in what is returned one can use addScalar().
sess.createSQLQuery("SELECT * FROM CATS") .addScalar("ID", Hibernate.LONG) .addScalar("NAME", Hibernate.STRING) .addScalar("BIRTHDATE", Hibernate.DATE)
This query specified: the SQL query string the columns and types to return
This will still return Object arrays, but now it will not use ResultSetMetdata but will instead explicitly get the ID, NAME and BIRTHDATE column as respectively a Long, String and a Short from the underlying resultset. This also means that only these three columns will be returned, even though the query is using * and could return more than the three listed columns. It is possible to leave out the type information for all or some of the scalars.
sess.createSQLQuery("SELECT * FROM CATS") .addScalar("ID", Hibernate.LONG) .addScalar("NAME") .addScalar("BIRTHDATE")
This is essentially the same query as before, but now ResultSetMetaData is used to decide the type of NAME and BIRTHDATE where as the type of ID is explicitly specified.
168
Native SQL
How the java.sql.Types returned from ResultSetMetaData is mapped to Hibernate types is controlled by the Dialect. If a specific type is not mapped or does not result in the expected type it is possible to customize it via calls to registerHibernateType in the Dialect.
This query specified: the SQL query string the entity returned by the query
Assuming that Cat is mapped as a class with the columns ID, NAME and BIRTHDATE the above queries will both return a List where each element is a Cat entity. If the entity is mapped with a many-to-one to another entity it is required to also return this when performing the native query, otherwise a database specific "column not found" error will occur. The additional columns will automatically be returned when using the * notation, but we prefer to be explicit as in the following example for a many-to-one to a Dog:
sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, DOG_ID FROM CATS").addEntity(Cat.class);
sess.createSQLQuery("SELECT c.ID, NAME, BIRTHDATE, DOG_ID, D_ID, D_NAME FROM CATS c, DOGS d WHERE c.DO .addEntity("cat", Cat.class) .addJoin("cat.dog");
In this example the returned Cat's will have their dog property fully initialized without any extra roundtrip to the database. Notice that we added a alias name ("cat") to be able to specify the target property path of the join. It is possible to do the same eager joining for collections, e.g. if the Cat had a one-to-many to Dog instead.
sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, D_ID, D_NAME, CAT_ID FROM CATS c, DOGS d WHERE c.ID = .addEntity("cat", Cat.class) .addJoin("cat.dogs");
<p>At this stage we are reaching the limits of what is possible with native queries without starting to enhance the sql queries to make them usable in Hibernate; the problems starts to arise when returning multiple entities of the same type or when the default alias/column names are not enough.</p>
169
Native SQL
Until now the result set column names are assumed to be the same as the column names specified in the mapping document. This can be problematic for SQL queries which join multiple tables, since the same column names may appear in more than one table. Column alias injection is needed in the following query (which most likely will fail):
sess.createSQLQuery("SELECT c.*, m.* .addEntity("cat", Cat.class) .addEntity("mother", Cat.class) FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
The intention for this query is to return two Cat instances per row, a cat and its mother. This will fail since there is a conflict of names since they are mapped to the same column names and on some databases the returned column aliases will most likely be on the form "c.ID", "c.NAME", etc. which are not equal to the columns specificed in the mappings ("ID" and "NAME"). The following form is not vulnerable to column name duplication:
sess.createSQLQuery("SELECT {cat.*}, {mother.*} .addEntity("cat", Cat.class) .addEntity("mother", Cat.class) FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
This query specified: the SQL query string, with placeholders for Hibernate to inject column aliases the entities returned by the query
The {cat.*} and {mother.*} notation used above is a shorthand for "all properties". Alternatively, you may list the columns explicity, but even in this case we let Hibernate inject the SQL column aliases for each property. The placeholder for a column alias is just the property name qualified by the table alias. In the following example, we retrieve Cats and their mothers from a different table (cat_log) to the one declared in the mapping metadata. Notice that we may even use the property aliases in the where clause if we like.
String sql = "SELECT ID as {c.id}, NAME as {c.name}, " + "BIRTHDATE as {c.birthDate}, MOTHER_ID as {c.mother}, {mother.*} " + "FROM CAT_LOG c, CAT_LOG m WHERE {c.mother} = c.ID"; List loggedCats = sess.createSQLQuery(sql) .addEntity("cat", Cat.class) .addEntity("mother", Cat.class).list()
16.1.4.1. Alias and property references For most cases the above alias injection is needed, but for queries relating to more complex mappings like composite properties, inheritance discriminators, collections etc. there are some specific aliases to use to allow Hibernate to inject the proper aliases. The following table shows the different possibilities of using the alias injection. Note: the alias names in the result are examples, each alias will have a unique and probably different name when used. Tabela 16.1. Alias injection names Description A simple property Syntax
{[aliasname].[prope rtyname]
Example
A_NAME as {item.name}
170
Example
CURRENCY as {item.amount.currency}, VALUE as
{item.amount.value}
Discriminator of an {[aliasname].class} DISC as {item.class} entity All properties of an {[aliasname].*} entity A collection key
{[aliasname].key} {item.*}
The element of an col- {[aliasname].elemen XID as {coll.element} lection t} roperty of the element {[aliasname].elemen NAME as {coll.element.name} in the collection t.[propertyname]} All properties of the {[aliasname].elemen {coll.element.*} element in the collecti- t.*} on All properties of the {[aliasname].*} the collection
{coll.*}
The above query will return a list of CatDTO which has been instantiated and injected the values of NAME and BIRTHNAME into its corresponding properties or fields.
16.1.7. Parameters
Native sql queries support positional as well as named parameters: Hibernate 3.2 cr2 171
Native SQL
Query query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like ?").addEntity(Cat.class); List pusList = query.setString(0, "Pus%").list(); query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like :name").addEntity(Cat.class); List pusList = query.setString("name", "Pus%").list();
The <return-join> and <load-collection> elements are used to join associations and define queries which initialize collections, respectively.
<sql-query name="personsWith"> <return alias="person" class="eg.Person"/> <return-join alias="address" property="person.mailingAddress"/> SELECT person.NAME AS {person.name}, person.AGE AS {person.age}, person.SEX AS {person.sex}, adddress.STREET AS {address.street}, adddress.CITY AS {address.city}, adddress.STATE AS {address.state}, adddress.ZIP AS {address.zip} FROM PERSON person JOIN ADDRESS adddress ON person.ID = address.PERSON_ID AND address.TYPE='MAILING' WHERE person.NAME LIKE :namePattern </sql-query>
A named SQL query may return a scalar value. You must declare the column alias and Hibernate type using the <return-scalar> element:
<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>
You can externalize the resultset mapping informations in a <resultset> element to either reuse them accross several named queries or through the setResultSetMapping() API.
<resultset name="personAddress"> <return alias="person" class="eg.Person"/> <return-join alias="address" property="person.mailingAddress"/>
172
Native SQL
</resultset> <sql-query name="personsWith" resultset-ref="personAddress"> SELECT person.NAME AS {person.name}, person.AGE AS {person.age}, person.SEX AS {person.sex}, adddress.STREET AS {address.street}, adddress.CITY AS {address.city}, adddress.STATE AS {address.state}, adddress.ZIP AS {address.zip} FROM PERSON person JOIN ADDRESS adddress ON person.ID = address.PERSON_ID AND address.TYPE='MAILING' WHERE person.NAME LIKE :namePattern </sql-query>
You can alternatively use the resultset mapping information in your hbm files directly in java code.
List cats = sess.createSQLQuery( "select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id" ) .setResultSetMapping("catAndKitten") .list();
also works with multiple columns. This solves a limitation with the {}-syntax which can not allow fine grained control of multi-column properties.
<return-property> <sql-query name="organizationCurrentEmployments"> <return alias="emp" class="Employment"> <return-property name="salary"> <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>
Notice that in this example we used <return-property> in combination with the {}-syntax for injection. Allowing users to choose how they want to refer column and properties.
173
Native SQL
If your mapping has a discriminator you must use <return-discriminator> to specify the discriminator column.
To use this query in Hibernate you need to map it via a named query.
<sql-query name="selectAllEmployees_SP" callable="true"> <return alias="emp" class="Employment"> <return-property name="employee" column="EMPLOYEE"/> <return-property name="employer" column="EMPLOYER"/> <return-property name="startDate" column="STARTDATE"/> <return-property name="endDate" column="ENDDATE"/> <return-property name="regionCode" column="REGIONCODE"/> <return-property name="id" column="EID"/> <return-property name="salary"> <return-column name="VALUE"/> <return-column name="CURRENCY"/> </return-property> </return> { ? = call selectAllEmployments() } </sql-query>
Notice stored procedures currently only return scalars and entities. <return-join> and <load-collection> are not supported. 16.2.2.1. Rules/limitations for using stored procedures To use stored procedures with Hibernate the procedures/functions have to follow some rules. If they do not follow those rules they are not usable with Hibernate. If you still want to use these procedures you have to execute them via session.connection(). The rules are different for each database, since database vendors have different stored procedure semantics/syntax. Stored procedure queries can't be paged with setFirstResult()/setMaxResults(). Recommended call form is standard SQL92: { ? = call functionName(<parameters>) } or { ? = call procedureName(<parameters>}. Native call syntax is not supported. For Oracle the following rules apply: A funp deve retornar um result set. O primeiro parmetro da procedure dever ser uma SA#DA que retorne um result set. Isto feito usando o tipo a SYS_REFCURSOR no Oracle 9 ou 10. No Oracle ne-
174
Native SQL cessrio definir o tipo REF CURSOR, veja a documentao do Oracle. For Sybase or MS SQL server the following rules apply: A procedure deve retornar um result set. Veja que este servidor pode retornar mltiplos result sets e update counts. O Hibernate ira iterar os resultados e pegar o primeiro resultado que o valor de retorno do result set. O resto ser descartado. Se voc habilitar SET NOCOUNT ON na sua procedure, ela provavelmente ser mais eficiente. Mas, isto no obrigatrio
O SQL executado diretamente no seu banco de dados, ento voc pode usar qualquer linguagem que quiser. Isto com certeza reduzira a portabilidade do seu mapeamento se voc utilizar um SQL para um banco de dados especifico. Stored Procedures so suportadas se o atributo the callable estiver ativado:
<class name="Person"> <id name="id"> <generator class="increment"/> </id> <property name="name" not-null="true"/> <sql-insert callable="true">{call createPerson (?, ?)}</sql-insert> <sql-delete callable="true">{? = call deletePerson (?)}</sql-delete> <sql-update callable="true">{? = call updatePerson (?, ?)}</sql-update> </class>
A ordem de posies dos parmetros so vitais, pois eles devem estar na mesma seqncia esperada pelo Hibernate. Voc pode ver a ordem esperada ativando o debug logging no n-vel org.hibernate.persister.entity. Com este n-vel ativado, o Hibernate ir imprimir o SQL esttico que foi usado para create, update, delete, etc. Entidades. (Para ver a seqncia esperada, lembre-se de no incluir seu SQL customizado no arquivo de mapeamento, pois ele ir sobreecreve o SQL esttico gerado pelo Hibernate). As stored procedures so na maioria dos casos (leia: melhor no fazer) requeridas para retornar o numero de linhas inseridas/atualizadas/deletadas. O hibernate tem algumas verificaes em tempo de execuo para o sucesso da declarao. Hibernate sempre registra o primeiro parmetro da declarao como uma sa-da numrica para operaes CRUD.
175
Native SQL
CREATE OR REPLACE FUNCTION updatePerson (uid IN NUMBER, uname IN VARCHAR2) RETURN NUMBER IS BEGIN update PERSON set NAME = uname, where ID = uid; return SQL%ROWCOUNT; END updatePerson;
Este apenas uma declarao de query com nome, como discutido anteriormente. Voc pode referenciar esta query com nome 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>
Isto tambm funciona com stored procedures. Voc pode tembm definir uma query para iniciar collection:
<set name="employments" inverse="true"> <key/> <one-to-many class="Employment"/> <loader query-ref="employments"/> </set>
<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>
You could even define an entity loader that loads a collection by join fetching:
<sql-query name="person"> <return alias="pers" class="Person"/> <return-join alias="emp" property="pers.employments"/> SELECT NAME AS {pers.*}, {emp.*} FROM PERSON pers
176
Native SQL
LEFT OUTER JOIN EMPLOYMENT emp ON pers.ID = emp.PERSON_ID WHERE ID=? </sql-query>
177
ou em uma coleo:
<set ...> <filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/> </set>
ou mesmo para ambos (ou muitos de cada) ao mesmo tempo. Os mtodos na Session so: enableFilter(String filterName), getEnabledFilter(String filterName), e disableFilter(String filterName). Por padro, os filtros no so habilitados dentro de qualquer session; Eles devem ser explicitamente habilitados usando o mtodo Session.enabledFilter(), que retorna uma instncia da interface Filter. Usando o filtro simples definido acima, o cdigo se pareceria com o seguinte:
session.enableFilter("myFilter").setParameter("myFilterParam", "some-value");
Veja que os mtodos da interface org.hibernate.Filter permite o encadeamento de funes, comum maioria das funes do Hibernate. Um exemplo completo, usando dados temporais com um padro efetivo de registro de datas:
<filter-def name="effectiveDate"> <filter-param name="asOfDate" type="date"/> </filter-def> <class name="Employee" ...> ...
178
Filtrando dados
<many-to-one name="department" column="dept_id" class="Department"/> <property name="effectiveStartDate" type="date" column="eff_start_dt"/> <property name="effectiveEndDate" type="date" column="eff_end_dt"/> ... <!-Note that this assumes non-terminal records have an eff_end_dt set to a max db date for simplicity-sake --> <filter name="effectiveDate" condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/> </class> <class name="Department" ...> ... <set name="employees" lazy="true"> <key column="dept_id"/> <one-to-many class="Employee"/> <filter name="effectiveDate" condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/> </set> </class>
Para garantir que voc sempre tenha registro efetivos, simplesmente habilite o filtro na session antes de recuperar os dados dos empregados:
Session session = ...; session.enabledFilter("effectiveDate").setParameter("asOfDate", new Date()); List results = session.createQuery("from Employee as e where e.salary > :targetSalary") .setLong("targetSalary", new Long(1000000)) .list();
No HQL acima, mesmo que mencionamos apenas uma restrio de salrio nos resultados, por causa do filtro habilitado, a consulta retornar apenas os funcionrios ativos cujo salrio maior que um milho de dlares. Nota: se voc planeja usar filtros com outer join (por HQL ou por load fetching) seja cuidadoso na direo da expresso de condio. mais seguro configura-lo com para um left outer join; geralmente, coloque o parmetro primeiro seguido pelo nome da coluna aps o operador.
179
180
Mapeamento XML
<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 grafo de de pares nome de propriedade/valor (Maps do Java). Os nomes de propriedades so somente construes lgicas que podem ser referenciadas em consultas HQL.
Para colees e associaes simples, existe o atributo adicional embed-xml. Se o atributo embed-xml="true", que o valor padro, a rvore XML para a entidade associada (ou coleo de determinado tipo de valor) ser embutida diretamente na rvore XML que contm a associao. Por outro lado, se embed-xml="false", ento apenas o valor do identificador referenciado ir aparecer no XML para associaes simples e colees simplesmenteno iro aparecer. Voc precisa tomar cuidado em no deixar oembed-xml="true" para muitas associaes, pois o XML no suporta bem referncias 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"
181
Mapeamento XML
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 embutir a coleno de account ids, e no os dados de accounts. A query HQL a seguir:
from Customer c left join fetch c.accounts where c.lastName like :lastName
182
Mapeamento XML
Session session = factory.openSession(); Session dom4jSession = session.getSession(EntityMode.DOM4J); Transaction tx = session.beginTransaction(); List results = dom4jSession .createQuery("from Customer c left join fetch c.accounts where c.lastName like :lastName") .list(); for ( int i=0; i<results.size(); i++ ) { //add the customer data to the XML document Element customer = (Element) results.get(i); doc.add(customer); } tx.commit(); session.close();
Session session = factory.openSession(); Session dom4jSession = session.getSession(EntityMode.DOM4J); Transaction tx = session.beginTransaction(); Element cust = (Element) dom4jSession.get("Customer", customerId); for ( int i=0; i<results.size(); i++ ) { Element customer = (Element) results.get(i); //change the customer name in the XML and database Element name = customer.element("name"); name.element("first-name").setText(firstName); name.element("initial").setText(initial); name.element("last-name").setText(lastName); } tx.commit(); session.close();
extremamente til combinar essa funcionalidade com a operao replicate() do Hibernate para implementar importao/exportao baseadas em XML.
183
O Hibernate distingue tambm entre: Immediate fetching - uma associao, coleo ou atributo buscado como ela carregada (Qual SQL usado). No se confuda com eles! Ns usamos fetch para melhorar a performance. Ns podemos usar lazy para definir um contrato para qual dado sempre disponvel em qualquer instncia desanexada de uma classe qualquer. imediatamente, quando o pai carregado. Lazy collection fetching - a coleo buscada quando a aplicao invoca uma operao sobre aquela coleo (Esse o padro para colees) "Extra-lazy" collection fetching - elementos individuais de uma coleo so acessados do banco de dados quando preciso. O Hibernate tenta no buscar a coleo inteira dentro da memria ao menos que seja absolutamente preciso. (indicado para colees muito grandes) Proxy fetching - uma associao de um valor carregada quando um mtodo diferente do getter do identificador invocado sobre o objeto associado. "No-proxy" fetching - uma associao de um valor carregada quando a varivel da instncia carregada. Comparada com a proxy fetching, esse mtodo menos preguioso (lazy)(a associao carregada somente quando o identificador acessada) mas mais transparente, j que no h proxies visveis para a aplicao. Esse mtodo requer instrumentao de bytecodes em build-time e raramente necessrio. Lazy attribute fetching - um atributo ou associao de um valor carregada quanto a varavel da instncia acessada. Esse mtodo requer instrumentao de bytecodes em build-time e raramente necessrio.
Ns temos aqui duas noes ortogonais: quando a associao carregada e como ela carregada (Qual SQL usado). No se confuda com eles! Ns usamos fetch para melhorar a performance. Ns podemos usar lazy para definir um contrato para qual dado sempre disponvel em qualquer instncia desconectada de uma classe
184
Como a coleo de permisses no foi inicializada quando a Session foi fechada, a coleo no poder carregar o seu estado. O Hibernate no suporta inicializao preguiosa para objetos desconectados. Para consertar isso, necessrio mover o cdigo que carrega a coleo para antes da transao ser comitada. Alternativamente, ns podemos usar uma coleo ou associao no preguiosa, especificando lazy="false" para o mapeamento da associao. Porm, pretendido que a inicializao preguiosa seja usada por quase todas as colees e associaes. Se voc definir muitas associaes no preguiosas em seu modelo de objetos, o Hibernate ir precisar carregar o banco de dados inteiro na memria em cada transao! Por outro lado, ns geralmente escolhemos join fetching (que no preguiosa por natureza) ao invs de select fetching em uma transao particular. Ns iremos ver como customizar a estratgoa de busca. No Hibernate3, os mecanismos para escolher a estratgia de fetching so identicos para as associaes simples e para colees.
A estratgia de fetch definida no documento de mapeamento afeta: recupera via get() ou load() Recuperaes que acontecem implicitamente quando navegamos por uma associao
185
Aumentando a performance
Criteria
queries
Independentemente da estratgia de busca que voc usar, o grafo no preguioso definido ser garantidamente carregado na memria. Note que isso ir resultar em diversos selects imediatos sendo usados em um HQL em particular. Usualmente no usamos documentos de mapeamento para customizar as buscas. Ao invs disso, ns deixamos o comportamento padro e sobrescrevemos isso em uma transao em particular, usando left join fetch no HQL. Isso diz ao Hibernate para buscar a associao inteira no primeiro select, usando um outer join. Na API de busca Criteria, voc ir usar setFetchMode(FetchMode.JOIN). Se voc quiser mudar a estratgia de busca usada pelo get() ou load(), simplesmente use uma query Criteria, por exemplo:
User user = (User) session.createCriteria(User.class) .setFetchMode("permissions", FetchMode.JOIN) .add( Restrictions.idEq(userId) ) .uniqueResult();
(Isto o equivalente do Hibernate para o que algumas solues ORM chamam de "plano de busca") Um meio totalmente diferente de evitar problemas com selects N+1 usar um cache de segundo nvel.
Primeiramente, instncias de Cat nunca sero convertidas para DomesticCat, mesmo que a instncia em questo seja uma estncia de DomesticCat:
Cat cat = (Cat) session.load(Cat.class, id); if ( cat.isDomesticCat() ) { DomesticCat dc = (DomesticCat) cat; // instantiate a proxy (does not hit the db) // hit the db to initialize the proxy // Error!
186
Aumentando a performance
.... }
Porm a situao no to ruim como parece. Mesmo quando temos duas referncias para objetos proxies diferentes, a instncia deles ser o mesmo objeto
cat.setWeight(11.0); // hit the db to initialize the proxy System.out.println( dc.getWeight() ); // 11.0
Terceiro, Voc no pode usar um proxy CGLIB em uma classe final ou com qualquer mtodo final. Finalmente, se o seu objeto persistente adquirir qualquer recursto durante a instanciao (em inicializadores ou construtor padro), ento esses recursos sero adquiridos pelo proxy tambm. A classe de proxy uma subclasse da classe persistente. Esses problemas so todos devido a limitao fundamental do modelo de herana simples do Java. Se voc quiser evitar esse problemas em suas classes persistentes voc deve imeplementar uma interface que declare seus mtodos de negcio. Voc deve especificar essas interfaces no arquivo de mapeamento. Ex:
<class name="CatImpl" proxy="Cat"> ...... <subclass name="DomesticCatImpl" proxy="DomesticCat"> ..... </subclass> </class>
onde CatImpl implementa a interface Cat e DomesticCatImpl implementa a interface DomesticCat. Ento proxies para instncias de Cat e DomesticCat sero retornadas por load() ou iterate(). (Note que list() geralmente no retorna proxies).
Cat cat = (Cat) session.load(CatImpl.class, catid); Iterator iter = session.iterate("from CatImpl as cat where cat.name='fritz'"); Cat fritz = (Cat) iter.next();
Relacionamentos so tambm carregados preguiosamente. Isso significa que voc precisa declarar qualquer propriedade como sendo do tipo Cat, e no CatImpl. Algumas operaes no requerem inicializao por proxy:
equals(), hashCode(),
se a classe persistente no sobrescrever equals() se a classe persistente no sobrescrever hashCode() O mtodo getter do identificador
O Hibernate ir detectar classes persistentes que sobrescrevem equals() ou hashCode(). Escolhendo lazy="no-proxy" ao invs do padro lazy="proxy", podemos evitar problemas associados com typecasting. Porm, iremos precisar de instrumentao de bytecode em tempo de compilao e todas as operaes iro resultar em iniciazaes de proxy imediatas.
187
Aumentando a performance
As vezes voc no quer inicializar uma coleo muito grande, mas precisa de algumas informaes (como o tamanho) ou alguns de seus dados. Voc pode usar um filtro de coleo para saber seu tamanho sem a inicializar:
( (Integer) s.createFilter( collection, "select count(*)" ).list().get(0) ).intValue()
O mtodo createFilter() usado tambm para retornar algus dados de uma coleo eficientemente sem precisar inicializar a coleo inteira:
s.createFilter( lazyCollection, "").setFirstResult(0).setMaxResults(10).list();
188
Aumentando a performance
O Hibernate ir executar agora apenas trs consultas, buscando por vez, 10, 10 e 5 Person. Voc tambm pode habilitar busca em lote de uma coleo. Por exemplo, se cada Person tem uma coleo preguiosa de Cats, e 10 pessoas esto j carregados em uma Sesssion, sero gerados 10 SELECTs ao se iterar todas as pessoas, um para cada chamada de getCats().. Se voc habilitar busca em lote para a coleo de cats no mapeamento da classe Person, o Hibernate pode fazer uma pr carga das colees:
<class name="Person"> <set name="cats" batch-size="3"> ... </set> </class>
Com um batch-sizede 8, o Hibernate ir carregar 3, 3, 3, 1 colees em 4 SELECTs. Novamente, o valor do atributo depende do nmero esperado de colees no inicialiadas em determinada Session. A busca em lote de colees particularmente til quando voc tem uma rvore encadeada de items, ex. o tpico padro bill-of-materials (Se bem que um conjunto encadeado ou caminho materializado pode ser uma opo melhor para rvores com mais leitura)
189
Aumentando a performance
</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>
A carga posterior de propriedades requer instrumentao de bytecode! Se suas classes persistentes no forem melhoradas, o Hibernate ir ignorar silenciosamente essa configurao e usar busca imediatamente. Para instrumentao de bytecode, use a seguinte tarefa do Ant:
<target name="instrument" depends="compile"> <taskdef name="instrument" classname="org.hibernate.tool.instrument.InstrumentTask"> <classpath path="${jar.path}"/> <classpath path="${classes.dir}"/> <classpath refid="lib.class.path"/> </taskdef> <instrument verbose="true"> <fileset dir="${testclasses.dir}/org/hibernate/auction/model"> <include name="*.class"/> </fileset> </instrument> </target>
A different (better?) way to avoid unnecessary column reads, at least for read-only transactions is to use the projection features of HQL or Criteria queries. This avoids the need for buildtime bytecode processing and is certainly a prefered solution. You may force the usual eager fetching of properties using fetch all properties in HQL.
org.hibernate.cache.HashtableCacheProv ider
org.hibernate.cache.EhCacheProvider
memory, disk
yes
190
Aumentando a performance Cache OSCache SwarmCache JBoss TreeCache Provider class Type memory, disk clustered (ip multicast) clustered (ip multicast), transactional yes (clustered invalidation) yes (replication) yes (clock sync req.) Cluster Safe Query Cache Supported yes
org.hibernate.cache.OSCacheProvider org.hibernate.cache.SwarmCacheProvider
org.hibernate.cache.TreeCacheProvider
(1)
usage
read-only
(2)
(3)
(optional, defaults to the class or collection role name) specifies the name of the second level cache region include (optional, defaults to all) non-lazy specifies that properties of the entity mapped with lazy="true" may not be cached when attribute-level lazy fetching is enabled
region
Alternatively (preferrably?), you may specify <class-cache> and <collection-cache> elements in hibernate.cfg.xml. The usage attribute specifies a cache concurrency strategy.
191
Aumentando a performance
<cache usage="read-write"/> .... <set name="kittens" ... > <cache usage="read-write"/> .... </set> </class>
Hashtable (not inyes tended for production use) EHCache OSCache SwarmCache JBoss TreeCache yes yes yes yes
yes yes
yes
192
Aumentando a performance
The Session also provides a contains() method to determine if an instance belongs to the session cache. To completely evict all objects from the session cache, call Session.clear() For the second-level cache, there are methods defined on SessionFactory for evicting the cached state of an instance, entire class, collection instance or entire collection role.
sessionFactory.evict(Cat.class, catId); //evict a particular Cat sessionFactory.evict(Cat.class); //evict all Cats sessionFactory.evictCollection("Cat.kittens", catId); //evict a particular collection of kittens sessionFactory.evictCollection("Cat.kittens"); //evict all kitten collections
The CacheMode controls how a particular session interacts with the second-level cache.
CacheMode.NORMAL CacheMode.GET
- read items from the second-level cache, but don't write to the second-level cache except when updating data
CacheMode.PUT
- write items to the second-level cache, but don't read from the second-level cache
- write items to the second-level cache, but don't read from the second-level cache, bypass the effect of hibernate.cache.use_minimal_puts, forcing a refresh of the second-level cache for all items read from the database
CacheMode.REFRESH
To browse the contents of a second-level or query cache region, use the Statistics API:
Map cacheEntries = sessionFactory.getStatistics() .getSecondLevelCacheStatistics(regionName) .getEntries();
You'll need to enable statistics, and, optionally, force Hibernate to keep the cache entries in a more humanunderstandable format:
hibernate.generate_statistics true hibernate.cache.use_structured_entries true
This setting causes the creation of two new cache regions - one holding cached query result sets (org.hibernate.cache.StandardQueryCache), the other holding timestamps of the most recent updates to queryable tables (org.hibernate.cache.UpdateTimestampsCache). Note that the query cache does not cache the state of the actual entities in the result set; it caches only identifier values and results of value type. So the query cache should always be used in conjunction with the second-level cache. Hibernate 3.2 cr2 193
Aumentando a performance
Most queries do not benefit from caching, so by default queries are not cached. To enable caching, call Query.setCacheable(true). This call allows the query to look for existing cache results or add its results to the cache when it is executed. If you require fine-grained control over query cache expiration policies, you may specify a named cache region for a particular query by calling Query.setCacheRegion().
List blogs = sess.createQuery("from Blog blog where blog.blogger = :blogger") .setEntity("blogger", blogger) .setMaxResults(15) .setCacheable(true) .setCacheRegion("frontpages") .list();
If
refresh of its query cache region, you should call Query.setCacheMode(CacheMode.REFRESH). This is particularly useful in cases where underlying data may have been updated via a separate process (i.e., not modified through Hibernate) and allows the application to selectively refresh particular query result sets. This is a more efficient alternative to eviction of a query cache region via SessionFactory.evictQueries().
the
query
should
force
19.5.1. Taxonomy
Hibernate defines three basic kinds of collections: collections of values one to many associations many to many associations
This classification distinguishes the various table and foreign key relationships but does not tell us quite everything we need to know about the relational model. To fully understand the relational structure and performance characteristics, we must also consider the structure of the primary key that is used by Hibernate to update or delete collection rows. This suggests the following classification: indexed collections sets bags
All indexed collections (maps, lists, arrays) have a primary key consisting of the <key> and <index> columns. In this case collection updates are usually extremely efficient - the primary key may be efficiently indexed and a particular row may be efficiently located when Hibernate tries to update or delete it. Sets have a primary key consisting of <key> and element columns. This may be less efficient for some types of collection element, particularly composite elements or large text or binary fields; the database may not be able
194
Aumentando a performance to index a complex primary key as efficently. On the other hand, for one to many or many to many associations, particularly in the case of synthetic identifiers, it is likely to be just as efficient. (Side-note: if you want SchemaExport to actually create the primary key of a <set> for you, you must declare all columns as notnull="true".)
<idbag>
mappings define a surrogate key, so they are always very efficient to update. In fact, they are the best
case. Bags are the worst case. Since a bag permits duplicate element values and has no index column, no primary key may be defined. Hibernate has no way of distinguishing between duplicate rows. Hibernate resolves this problem by completely removing (in a single DELETE) and recreating the collection whenever it changes. This might be very inefficient. Note that for a one-to-many association, the "primary key" may not be the physical primary key of the database table - but even in this case, the above classification is still useful. (It still reflects how Hibernate "locates" individual rows of the collection.)
19.5.2. Lists, maps, idbags and sets are the most efficient collections to update
From the discussion above, it should be clear that indexed collections and (usually) sets allow the most efficient operation in terms of adding, removing and updating elements. There is, arguably, one more advantage that indexed collections have over sets for many to many associations or collections of values. Because of the structure of a Set, Hibernate doesn't ever UPDATE a row when an element is "changed". Changes to a Set always work via INSERT and DELETE (of individual rows). Once again, this consideration does not apply to one to many associations. After observing that arrays cannot be lazy, we would conclude that lists, maps and idbags are the most performant (non-inverse) collection types, with sets not far behind. Sets are expected to be the most common kind of collection in Hibernate applications. This is because the "set" semantics are most natural in the relational model. However, in well-designed Hibernate domain models, we usually see that most collections are in fact oneto-many associations with inverse="true". For these associations, the update is handled by the many-to-one end of the association, and so considerations of collection update performance simply do not apply.
19.5.3. Bags and lists are the most efficient inverse collections
Just before you ditch bags forever, there is a particular case in which bags (and also lists) are much more performant than sets. For a collection with inverse="true" (the standard bidirectional one-to-many relationship idiom, for example) we can add elements to a bag or list without needing to initialize (fetch) the bag elements! This is because Collection.add() or Collection.addAll() must always return true for a bag or List (unlike a Set). This can make the following common code much faster.
Parent p = (Parent) sess.load(Parent.class, id); Child c = new Child(); c.setParent(p); p.getChildren().add(c); //no need to fetch the collection! sess.flush();
195
Aumentando a performance
Occasionally, deleting collection elements one by one can be extremely inefficient. Hibernate isn't completely stupid, so it knows not to do that in the case of an newly-empty collection (if you called list.clear(), for example). In this case, Hibernate will issue a single DELETE and we are done! Suppose we add a single element to a collection of size twenty and then remove two elements. Hibernate will issue one INSERT statement and two DELETE statements (unless the collection is a bag). This is certainly desirable. However, suppose that we remove eighteen elements, leaving two and then add thee new elements. There are two possible ways to proceed delete eighteen rows one by one and then insert three rows remove the whole collection (in one SQL DELETE) and insert all five current elements (one by one)
Hibernate isn't smart enough to know that the second option is probably quicker in this case. (And it would probably be undesirable for Hibernate to be that smart; such behaviour might confuse database triggers, etc.) Fortunately, you can force this behaviour (ie. the second strategy) at any time by discarding (ie. dereferencing) the original collection and returning a newly instantiated collection with all the current elements. This can be very useful and powerful from time to time. Of course, one-shot-delete does not apply to collections mapped inverse="true".
// MBean service registration for all SessionFactory's Hashtable tb = new Hashtable(); tb.put("type", "statistics"); tb.put("sessionFactory", "all"); ObjectName on = new ObjectName("hibernate", tb); // MBean object name StatisticsService stats = new StatisticsService(); // MBean implementation server.registerMBean(stats, on); // Register the MBean on the server
196
Aumentando a performance
TODO: This doesn't make sense: In the first case, we retrieve and use the MBean directly. In the second one, we must give the JNDI name in which the session factory is held before using it. Use hibernateStatsBean.setSessionFactoryJNDIName("my/JNDI/Name")
You can (de)activate the monitoring for a SessionFactory at configuration time, set hibernate.generate_statistics to false at runtime:
sf.getStatistics().setStatisticsEnabled(true)
or
hibernateStatsBe-
an.setStatisticsEnabled(true)
Statistics can be reset programatically using the clear() method. A summary can be sent to a logger (info level) using the logSummary() method.
19.6.2. Metrics
Hibernate provides a number of metrics, from very basic to the specialized information only relevant in certain scenarios. All available counters are described in the Statistics interface API, in three categories: Metrics related to the general Session usage, such as number of open sessions, retrieved JDBC connections, etc. Metrics related to he entities, collections, queries, and caches as a whole (aka global metrics), Detailed metrics related to a particular entity, collection, query or cache region.
For exampl,e you can check the cache hit, miss, and put ratio of entities, collections and queries, and the average time a query needs. Beware that the number of milliseconds is subject to approximation in Java. Hibernate is tied to the JVM precision, on some platforms this might even only be accurate to 10 seconds. Simple getters are used to access the global metrics (i.e. not tied to a particular entity, collection, cache region, etc.). You can access the metrics of a particular entity, collection or cache region through its name, and through its HQL or SQL representation for queries. Please refer to the Statistics, EntityStatistics, CollectionStatistics, SecondLevelCacheStatistics, and QueryStatistics API Javadoc for more information. The following code shows a simple example:
Statistics stats = HibernateUtil.sessionFactory.getStatistics(); double queryCacheHitCount = stats.getQueryCacheHitCount(); double queryCacheMissCount = stats.getQueryCacheMissCount(); double queryCacheHitRatio = queryCacheHitCount / (queryCacheHitCount + queryCacheMissCount); log.info("Query Hit ratio:" + queryCacheHitRatio); EntityStatistics entityStats = stats.getEntityStatistics( Cat.class.getName() ); long changes = entityStats.getInsertCount() + entityStats.getUpdateCount() + entityStats.getDeleteCount(); log.info(Cat.class.getName() + " changed " + changes + "times"
);
To work on all entities, collections, queries and region caches, you can retrieve the list of names of entities, collections, queries and region caches with the following methods: getQueries(), getEntityNames(), getCol-
197
Aumentando a performance
lectionRoleNames(),
and getSecondLevelCacheRegionNames().
198
Please refer to the Hibernate Tools package and it's documentation for more information. However, the Hibernate main package comes bundled with an integrated tool (it can even be used from "inside" Hibernate on-the-fly): SchemaExport aka hbm2ddl.
Some tags also accept a not-null attribute (for generating a NOT NULL constraint on table columns) and a unique attribute (for generating UNIQUE constraint on table columns).
<many-to-one name="bar" column="barId" not-null="true"/>
199
Toolset Guide
A unique-key attribute may be used to group columns in a single unique key constraint. Currently, the specified value of the unique-key attribute is not used to name the constraint in the generated DDL, only to group the columns in the mapping file.
<many-to-one name="org" column="orgId" unique-key="OrgEmployeeId"/> <property name="employeeId" unique-key="OrgEmployee"/>
An index attribute specifies the name of an index that will be created using the mapped column or columns. Multiple columns may be grouped into the same index, simply by specifying the same index name.
<property name="lastName" index="CustName"/> <property name="firstName" index="CustName"/>
A foreign-key attribute may be used to override the name of any generated foreign key constraint.
<many-to-one name="bar" column="barId" foreign-key="FKFooBar"/>
Many mapping elements also accept a child <column> element. This is particularly useful for mapping multicolumn types:
<property name="name" type="my.customtypes.Name"/> <column name="last" not-null="true" index="bar_idx" length="30"/> <column name="first" not-null="true" index="bar_idx" length="20"/> <column name="initial"/> </property>
The default attribute lets you specify a default value for a column (you should assign the same value to the mapped property before saving a new instance of the mapped class).
<property name="credits" type="integer" insert="false"> <column name="credits" default="10"/> </property>
The sql-type attribute allows the user to override the default mapping of a Hibernate type to SQL datatype.
<property name="balance" type="float"> <column name="balance" sql-type="decimal(13,3)"/> </property>
<class name="Foo" table="foos" check="bar < 100.0"> ... <property name="bar" type="float"/> </class>
Interpretation column length column decimal precision column decimal scale specfies that the column should be non-nullable specifies that the column should have a unique constraint specifies the name of a (multi-column) index specifies the name of a multi-column unique constraint specifies the name of the foreign key constraint generated for an association, for a <one-to-one>, <many-to-one>, <key>, or <many-to-many> mapping element. Note that inverse="true" sides will not be considered by SchemaExport. overrides the default column type (attribute of <column> element only) specify a default value for the column create an SQL check constraint on either column or table
sql-type
default check
The <comment> element allows you to specify comments for the generated schema.
<class name="Customer" table="CurCust"> <comment>Current customers only</comment> ... </class>
This results in a comment on table or comment on column statement in the generated DDL (where supported).
Description don't output the script to stdout only drop the tables only create the tables 201
Description don't export to the database output the ddl script to a file select a NamingStrategy read Hibernate configuration from an XML file read database properties from a file format the generated SQL nicely in the script set an end of line delimiter for the script
20.1.3. Properties
Database properties may be specified as system properties with -D<property> in hibernate.properties in a named properties file with --properties
The needed properties are: Tabela 20.3. SchemaExport Connection Properties Property Name
hibernate.connection.driver_class hibernate.connection.url hibernate.connection.username hibernate.connection.password hibernate.dialect
Description jdbc driver class jdbc url database user user password dialect
202
Toolset Guide
drop="no" delimiter=";" output="schema-export.sql"> <fileset dir="src"> <include name="**/*.hbm.xml"/> </fileset> </schemaexport> </target>
Description don't output the script to stdout don't export the script to the database select a NamingStrategy read database properties from a file specify a .cfg.xml file
Description select a NamingStrategy read database properties from a file specify a .cfg.xml file
204
Instead, the default behaviour is that adding an entity to a collection merely creates a link between the two entities, while removing it removes the link. This is very appropriate for all sorts of cases. Where it is not appropriate at all is the case of a parent / child relationship, where the life of the child is bound to the lifecycle of the parent.
205
Example: Parent/Child
an INSERT to create the record for c an UPDATE to create the link from p to c
This is not only inefficient, but also violates any NOT NULL constraint on the parent_id column. We can fix the nullability constraint violation by specifying not-null="true" in the collection mapping:
<set name="children"> <key column="parent_id" not-null="true"/> <one-to-many class="Child"/> </set>
However, this is not the recommended solution. The underlying cause of this behaviour is that the link (the foreign key parent_id) from p to c is not considered part of the state of the Child object and is therefore not created in the INSERT. So the solution is to make the link part of the Child mapping.
<many-to-one name="parent" column="parent_id" not-null="true"/>
(We also need to add the parent property to the Child class.) Now that the Child entity is managing the state of the link, we tell the collection not to update the link. We use the inverse attribute.
<set name="children" inverse="true"> <key column="parent_id"/> <one-to-many class="Child"/> </set>
And now, only one SQL INSERT would be issued! To tighten things up a bit, we could create an addChild() method of Parent.
public void addChild(Child c) { c.setParent(this); children.add(c); }
Example: Parent/Child
The explicit call to save() is still annoying. We will address this by using cascades.
<set name="children" inverse="true" cascade="all"> <key column="parent_id"/> <one-to-many class="Child"/> </set>
Similarly, we don't need to iterate over the children when saving or deleting a Parent. The following removes p and all its children from the database.
Parent p = (Parent) session.load(Parent.class, pid); session.delete(p); session.flush();
will not remove c from the database; it will ony remove the link to p (and cause a NOT NULL constraint violation, in this case). You need to explicitly delete() the Child.
Parent p = (Parent) session.load(Parent.class, pid); Child c = (Child) p.getChildren().iterator().next(); p.getChildren().remove(c); session.delete(c); session.flush();
Now, in our case, a Child can't really exist without its parent. So if we remove a Child from the collection, we really do want it to be deleted. For this, we must use cascade="all-delete-orphan".
<set name="children" inverse="true" cascade="all-delete-orphan"> <key column="parent_id"/> <one-to-many class="Child"/> </set>
Note: even though the collection mapping specifies inverse="true", cascades are still processed by iterating the collection elements. So if you require that an object be saved, deleted or updated by cascade, you must add it to the collection. It is not enough to simply call setParent().
Example: Parent/Child children are new. (See Seo 10.7, Automatic state detection.) In Hibernate3, it is no longer necessary to specify an unsaved-value explicitly. The following code will update parent and child and insert newChild.
//parent and child were both loaded in a previous session parent.addChild(child); Child newChild = new Child(); parent.addChild(newChild); session.update(parent); session.flush();
Well, that's all very well for the case of a generated identifier, but what about assigned identifiers and composite identifiers? This is more difficult, since Hibernate can't use the identifier property to distinguish between a newly instantiated object (with an identifier assigned by the user) and an object loaded in a previous session. In this case, Hibernate will either use the timestamp or version property, or will actually query the second-level cache or, worst case, the database, to see if the row exists.
21.5. Conclusion
There is quite a bit to digest here and it might look confusing first time around. However, in practice, it all works out very nicely. Most Hibernate applications use the parent / child pattern in many places. We mentioned an alternative in the first paragraph. None of the above issues exist in the case of <composite-element> mappings, which have exactly the semantics of a parent / child relationship. Unfortunately, there are two big limitations to composite element classes: composite elements may not own collections, and they should not be the child of any entity other than the unique parent.
208
package eg; import java.text.DateFormat; import java.util.Calendar; public class BlogItem { private Long _id; private Calendar _datetime; private String _text; private String _title; private Blog _blog; public Blog getBlog() { return _blog; } public Calendar getDatetime() { return _datetime; } public Long getId() { return _id; } public String getText() { return _text; } public String getTitle() { return _title; } public void setBlog(Blog blog) { _blog = blog;
209
} public void setDatetime(Calendar calendar) { _datetime = calendar; } public void setId(Long long1) { _id = long1; } public void setText(String string) { _text = string; } public void setTitle(String string) { _title = string; } }
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="eg">
210
<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"/> </class> </hibernate-mapping>
public class BlogMain { private SessionFactory _sessions; public void configure() throws HibernateException { _sessions = new Configuration() .addClass(Blog.class) .addClass(BlogItem.class) .buildSessionFactory(); }
211
public void exportTables() throws HibernateException { Configuration cfg = new Configuration() .addClass(Blog.class) .addClass(BlogItem.class); new SchemaExport(cfg).create(true, true); } public Blog createBlog(String name) throws HibernateException { Blog blog = new Blog(); blog.setName(name); blog.setItems( new ArrayList() ); Session session = _sessions.openSession(); Transaction tx = null; try { tx = session.beginTransaction(); session.persist(blog); tx.commit(); } catch (HibernateException he) { if (tx!=null) tx.rollback(); throw he; } finally { session.close(); } return blog; } public BlogItem createBlogItem(Blog blog, String title, String text) throws HibernateException { BlogItem item = new BlogItem(); item.setTitle(title); item.setText(text); item.setBlog(blog); item.setDatetime( Calendar.getInstance() ); blog.getItems().add(item); Session session = _sessions.openSession(); Transaction tx = null; try { tx = session.beginTransaction(); session.update(blog); tx.commit(); } catch (HibernateException he) { if (tx!=null) tx.rollback(); throw he; } finally { session.close(); } return item; } public BlogItem createBlogItem(Long blogid, String title, String text) throws HibernateException { BlogItem item = new BlogItem(); item.setTitle(title); item.setText(text); item.setDatetime( Calendar.getInstance() ); Session session = _sessions.openSession(); Transaction tx = null; try { tx = session.beginTransaction(); Blog blog = (Blog) session.load(Blog.class, blogid); item.setBlog(blog);
212
blog.getItems().add(item); tx.commit(); } catch (HibernateException he) { if (tx!=null) tx.rollback(); throw he; } finally { session.close(); } return item; } public void updateBlogItem(BlogItem item, String text) throws HibernateException { item.setText(text); Session session = _sessions.openSession(); Transaction tx = null; try { tx = session.beginTransaction(); session.update(item); tx.commit(); } catch (HibernateException he) { if (tx!=null) tx.rollback(); throw he; } finally { session.close(); } } public void updateBlogItem(Long itemid, String text) throws HibernateException { Session session = _sessions.openSession(); Transaction tx = null; try { tx = session.beginTransaction(); BlogItem item = (BlogItem) session.load(BlogItem.class, itemid); item.setText(text); tx.commit(); } catch (HibernateException he) { if (tx!=null) tx.rollback(); throw he; } finally { session.close(); } } public List listAllBlogNamesAndItemCounts(int max) throws HibernateException { Session session = _sessions.openSession(); Transaction tx = null; List result = null; try { tx = session.beginTransaction(); Query q = session.createQuery( "select blog.id, blog.name, count(blogItem) " + "from Blog as blog " + "left outer join blog.items as blogItem " + "group by blog.name, blog.id " + "order by max(blogItem.datetime)" ); q.setMaxResults(max); result = q.list();
213
tx.commit(); } catch (HibernateException he) { if (tx!=null) tx.rollback(); throw he; } finally { session.close(); } return result; } public Blog getBlogAndAllItems(Long blogid) throws HibernateException { Session session = _sessions.openSession(); Transaction tx = null; Blog blog = null; try { tx = session.beginTransaction(); Query q = session.createQuery( "from Blog as blog " + "left outer join fetch blog.items " + "where blog.id = :blogid" ); q.setParameter("blogid", blogid); blog = (Blog) q.uniqueResult(); tx.commit(); } catch (HibernateException he) { if (tx!=null) tx.rollback(); throw he; } finally { session.close(); } return blog; } public List listBlogsAndRecentItems() throws HibernateException { Session session = _sessions.openSession(); Transaction tx = null; List result = null; try { tx = session.beginTransaction(); Query q = session.createQuery( "from Blog as blog " + "inner join blog.items as blogItem " + "where blogItem.datetime > :minDate" ); Calendar cal = Calendar.getInstance(); cal.roll(Calendar.MONTH, false); q.setCalendar("minDate", cal); result = q.list(); tx.commit(); } catch (HibernateException he) { if (tx!=null) tx.rollback(); throw he; } finally { session.close(); } return result; } }
214
23.1. Employer/Employee
O modelo de seguinte relacionamento entre Employer e Employee utiliza uma entidade de classe atual (Employment) para representar a associao. Isto feito porque pode-ser ter mais do que um perodo de trabalho para as duas partes envolvidas. Outros Componentes so usados para modelar valores monetrios e os nomes do empregado.
215
<id name="id"> <generator class="sequence"> <param name="sequence">employee_id_seq</param> </generator> </id> <property name="taxfileNumber"/> <component name="name" class="Name"> <property name="firstName"/> <property name="initial"/> <property name="lastName"/> </component> </class> </hibernate-mapping>
23.2. Author/Work
Considere o seguinte modelo de relacionamento entre Work, Author e Person. Ns representamos o relacionamento entre Work e Author como uma associao muitos-para-muitos. Ns escolhemos representar o relacionamento entre Author e Person como uma associao um-para-um. Outra possibilidade seria ter Author extendendo Person.
216
217
<class name="Person" table="persons"> <id name="id" column="id"> <generator class="native"/> </id> <property name="name"/> </class> </hibernate-mapping>
Existem quatro tabelas neste mapeamento. works, authors e persons recebem os dados de work, author e person, respectivamente. author_work uma tabela de associao que liga authors works. Abaixo o esquema das tabelas, gerados pelo SchemaExport.
create table works ( id BIGINT not null generated by default as identity, tempo FLOAT, genre VARCHAR(255), text INTEGER, title VARCHAR(255), type CHAR(1) not null, primary key (id) ) create table author_work ( author_id BIGINT not null, work_id BIGINT not null, primary key (work_id, author_id) ) create table authors ( id BIGINT not null generated by default as identity, alias VARCHAR(255), primary key (id) ) create table persons ( id BIGINT not null generated by default as identity, name VARCHAR(255), primary key (id) ) alter table authors add constraint authorsFK0 foreign key (id) references persons alter table author_work add constraint author_workFK0 foreign key (author_id) references authors alter table author_work add constraint author_workFK1 foreign key (work_id) references works
23.3. Customer/Order/Product
Agora considere um modelo de relacionamento entre Customer, Order e LineItem e Product. Existe uma associao um-para-muitos entre Customer e Order, mas como devemos representar Order / LineItem / Product? Eu escolhi mapear LineItem como uma classe de associao representando a associao muitos-para-muitos entre Order and Product. No Hibernate, isto conhecido como um elemento composto.
218
O cdigo do mapeamento:
<hibernate-mapping> <class name="Customer" table="customers"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <set name="orders" inverse="true"> <key column="customer_id"/> <one-to-many class="Order"/> </set> </class> <class name="Order" table="orders"> <id name="id"> <generator class="native"/> </id> <property name="date"/> <many-to-one name="customer" column="customer_id"/> <list name="lineItems" table="line_items"> <key column="order_id"/> <list-index column="line_number"/> <composite-element class="LineItem"> <property name="quantity"/> <many-to-one name="product" column="product_id"/> </composite-element> </list> </class> <class name="Product" table="products"> <id name="id"> <generator class="native"/> </id> <property name="serialNumber"/> </class> </hibernate-mapping> customers, orders, line_items
e products recebem os dados de customer, order, line_item e product, respectivamente. line_items tambm atua como uma tabela de associao ligando orders com products.
create table customers ( id BIGINT not null generated by default as identity, name VARCHAR(255), primary key (id) ) create table orders ( id BIGINT not null generated by default as identity, customer_id BIGINT, date TIMESTAMP, primary key (id) ) create table line_items ( line_number INTEGER not null,
219
order_id BIGINT not null, product_id BIGINT, quantity INTEGER, primary key (order_id, line_number) ) create table products ( id BIGINT not null generated by default as identity, serialNumber VARCHAR(255), primary key (id) ) alter table orders add constraint ordersFK0 foreign key (customer_id) references customers alter table line_items add constraint line_itemsFK0 foreign key (product_id) references products alter table line_items add constraint line_itemsFK1 foreign key (order_id) references orders
220
</id> <property name="name" not-null="true" length="100"/> <property name="address" not-null="true" length="200"/> <list name="orders" inverse="true" cascade="save-update"> <key column="customerId"/> <index column="orderNumber"/> <one-to-many class="Order"/> </list> </class> <class name="Order" table="CustomerOrder" lazy="true"> <synchronize table="LineItem"/> <synchronize table="Product"/> <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> <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"
221
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>
222
<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>
223
</id> <property name="name" length="100"/> <one-to-one name="address" property-ref="person" cascade="all" fetch="join"/> <set name="accounts" inverse="true"> <key column="userId" property-ref="userId"/> <one-to-many class="Account"/> </set> <property name="userId" length="8"/> </class> <class name="Address"> <id name="id"> <generator class="hilo"/> </id> <property name="address" length="300"/> <property name="zip" length="5"/> <property name="country" length="25"/> <many-to-one name="person" unique="true" not-null="true"/> </class> <class name="Account"> <id name="accountId" length="32"> <generator class="uuid"/> </id> <many-to-one name="user" column="userId" property-ref="userId"/> <property name="type" not-null="true"/> </class>
224
Boas prticas uma transao particular. Em uma arquitetura de trs camadas, considere o uso de objetos separados. When using a servlet / session bean architecture, you could pass persistent objects loaded in the session bean to and from the servlet / JSP layer. Use a new session to service each request. Use Session.merge() or Session.saveOrUpdate() to synchronize objects with the database. In a two tiered architecture, consider using long persistence contexts. Database Transactions have to be as short as possible for best scalability. However, it is often neccessary to implement long running application transactions, a single unit-of-work from the point of view of a user. An application transaction might span several client request/response cycles. It is common to use detached objects to implement application transactions. An alternative, extremely appropriate in two tiered architecture, is to maintain a single open persistence contact (session) for the whole lifecycle of the application transaction and simply disconnect from the JDBC connection at the end of each request and reconnect at the beginning of the subsequent request. Never share a single session across more than one application transaction, or you will be working with stale data. Don't treat exceptions as recoverable. This is more of a necessary practice than a "best" practice. When an exception occurs, roll back the Transaction and close the Session. If you don't, Hibernate can't guarantee that in-memory state accurately represents persistent state. As a special case of this, do not use Session.load() to determine if an instance with the given identifier exists on the database; use Session.get() or a query instead. Prefer lazy fetching for associations. Use eager fetching sparingly. Use proxies and lazy collections for most associations to classes that are not likely to be completely held in the second-level cache. For associations to cached classes, where there is an a extremely high probability of a cache hit, explicitly disable eager fetching using lazy="false". When an join fetching is appropriate to a particular use case, use a query with a left join fetch. Use the open session in view pattern, or a disciplined assembly phase to avoid problems with unfetched data. Hibernate frees the developer from writing tedious Data Transfer Objects (DTO). In a traditional EJB architecture, DTOs serve dual purposes: first, they work around the problem that entity beans are not serializable; second, they implicitly define an assembly phase where all data to be used by the view is fetched and marshalled into the DTOs before returning control to the presentation tier. Hibernate eliminates the first purpose. However, you will still need an assembly phase (think of your business methods as having a strict contract with the presentation tier about what data is available in the detached objects) unless you are prepared to hold the persistence context (the session) open across the view rendering process. This is not a limitation of Hibernate! It is a fundamental requirement of safe transactional data access. Consider abstracting your business logic from Hibernate. Hide (Hibernate) data-access code behind an interface. Combine the DAO and Thread Local Session patterns. You can even have some classes persisted by handcoded JDBC, associated to Hibernate via a UserType. (This advice is intended for "sufficiently large" applications; it is not appropriate for an application with five tables!) Don't use exotic association mappings. Good usecases for a real many-to-many associations are rare. Most of the time you need additional information stored in the "link table". In this case, it is much better to use two one-to-many associations to an intermediate link class. In fact, we think that most associations are one-to-many and many-to-one, you should be careful when using any other association style and ask yourself if it is really neccessary. Prefer bidirectional associations. Unidirectional associations are more difficult to query. In a large application, almost all associations must be navigable in both directions in queries.
226