Curso De Hibernate 3

Post on 16-Nov-2014

18.715 views 20 download

description

 

Transcript of Curso De Hibernate 3

Curso: Persistência em Java com Curso: Persistência em Java com HibernateHibernate

Instrutor: Fabrício Lemos 23/04/2007

Persistência

É um tópico vital para o desenvolvimento de aplicações

Quase todas as aplicações necessitam que dados sejam persistidos

Necessidades Armazenamento Busca Organização Compartilhamento dos dados

Persistência

Necessidades Integridade dos dados Controle de concorrência

Performance e a escalabilidade são fortemente afetadas pela estratégia de acesso a dados escolhida

Banco de Dados Relacionais

Geralmente são utilizados banco de dados SQL Flexível Robusto Eficiente Confiável Maneira mais utilizada e conhecida de armazenar

dados

Banco de Dados Relacionais

Dados são armazenados de forma independente Independência de linguagem Independência de aplicação

Os dados geralmente possuem longevidade maior do que as aplicações que os acessam

A utilização de algum framework não elimina a necessidade de conhecimento de SQL e do modelo relacional

Persistindo dados com Java

Realizado através da API Java Database Connectivity (JDBC)

Tarefas de baixo nível Preparar consultas Associar parâmetros Executar a consulta Percorrer e retornar o resultado

Tarefas de alto nível Salvar e recuperar objetos

Persistindo objetos com Java(ou qualquer outra linguagem OO)

Linguagem orientada a objetos Deve-se poder armazenar o estado de um objeto,

mesmo após o objeto ser destruído Deve ser possível criar um novo objeto com o

mesmo estado do anterior Operações não devem ser limitadas a um único

objeto Associações devem ser salvas

Persistindo objetos com Java

A aplicação deve trabalhar diretamente com objetos, ao invés de linhas e colunas da base de dados

Lógica de negócio deve ser implementada na aplicação, utilizando-se Java, e não diretamente na base de dados Limitar o uso de stored procedures

Conceitos da Orientação a Objetos não devem ser restringidos pela solução adotada

Diferença dos Paradigmas: OO/Relacional

Granularidade Objetos de baixa granularidade podem ser persistidos

em tabelas de grande granularidade ou vice-versa Uma tabela armazena diversos tipos de objetos Um objeto é armazenado em diversas tabelas

Herança Modelo relacional não possui o conceito de herança

Diferença dos Paradigmas

Polimorfismo Necessidade de um objeto referenciar outros através

de superclasses Uma referência pode estar associada o objetos de

tipos diferentes Chaves estrangeiras referenciam uma tabela

específica

Diferença dos Paradigmas OO/Relacional

Identidade dos objetos Java

Operador == Método equals()

Banco de dados Chave primária Atualização de algum atributo que faz parte da chave

requer que outras tabelas sejam atualizadas

Diferença dos Paradigmas OO/Relacional

Associações OO possui associações unidirecionais e bidirecionais Junções de tabelas e projeções não possuem o

conceito de “direção” de uma associação Associações em OO podem ser do tipo many-to-many Associações entre tabelas só podem ser one-to-many e

one-to-one Necessidade de criar uma tabela de relacionamento para

associações many-to-many Tabela não presente no modelo do domínio da aplicação

Diferença dos Paradigmas: Custo

Necessário escrever muito código para (tentar) contornar o problema

Código se torna repetitivo e de difícil manutenção A escrita de código SQL pode tornar a aplicação

dependente do banco de dados Modelagem dos objetos fica prejudicada Outras camada ficam fortemente acopladas à

Camada de Persistência Produtividade pode ser fortemente afetada

Estratégias de Persistência

JDBC e SQL Faz parte da plataforma Java Necessário escrever bastante código de baixo nível

Stored Procedures Lógica de negócio sai da aplicação e vai para a base

de dados Perde-se a portabilidade

Estratégias de Persistência

Framework corporativo Necessário grande esforço da empresa Demora-se muito para que a solução implementada

atinga maturidade Documentação muitas vezes é esquecida Falta de suporte

Estratégias de Persistência

Java Persistence API (JPA) Especificação elaborada pelo Java Community

Process para persistência em Java Baseou-se em diversas soluções existentes Frameworks existentes passaram a implementar a

especificação Recursos são um sub-conjunto dos encontrados nos

frameworks que a implementam Atualizações são lentas e burocráticas

Estratégias de Persistência

Frameworks de terceiros TopLink

Framework de persistência Objeto/Relacional Desenvolvido pela Oracle Gratuito para avaliação e nas fases de desenvolvimento

Hibernate

Hibernate

Framework de mapeamento Objeto-Relacional Preenche a lacuna entre a base de dados

relacional e a aplicação orientada a objetos Framework de persistência Java mais utilizado e

documentado Mantido pela Jboss sob a licensa LGPL Suporta classes desenvolvidas com agregação,

herança, polimorfismo, composição e coleções

Hibernate

Permite a escrita de consultas tanto através de uma linguagem própria (HQL) como também através de SQL

Framework não intrusivo Não restringe a arquitetura da aplicação

Implementa a especificação Java Persistence API Grande e ativa comunidade

Mais de 25 mil desenvolvedores registrados nos foruns oficiais

Mapeamento Objeto Relacional

Permite a persistência de objetos em tabelas de uma base de dados relacional

Automática e Transparente Utiliza metadados para descrever o

relacionamento entre os objetos e a base de dados XML Xdoclet Annotations

Mapeamento Objeto Relacional

O SQL é gerado automaticamente a partir dos metadados

A escrita e manutenção de metadados necessita de esforço nas etapas de implementação Esforço bem menor do que o necessário para fazer a

conversão manualmente A conversão entre os tipos de representação pode

trazer perca de performance Ferramentas maduras otimizam a conversão em

diversos pontos

Mapeamento Objeto Relacional

Possui quatro partes principais API para realização de operações básicas (CRUD) Linguagem ou API para a realização de consultas Maneira para a especificação de metadados Técnicas de otimização

Dirty checking Carregamento tardio (lazy association fetching)

Mapeamento Objeto Relacional: Vantagens

Produtividade Elimina a necessidade de escrita de grande parte do

código relativo a persistência Maior tempo disponível para implementar a lógica da

aplicação Manutenibilidade

Menos código Maior entendimento da aplicação Camada de abstração Facilita a refatoração

Vantagens Performance

Tarefas manuais nem sempre tem performance melhor do que tarefas automatizadas

Considerando limitações de custo e tempo Frameworks maduros são bastantes otimizados Com o aumento de produtividade, você pode dedicar

mais tempo para resolver possíveis gargalos Portabilidade

Independência nem sempre é algo simples de ser alcançado, mesmo em aplicações Java

Suporte a diversos tipos de banco de dados

Hibernate: Módulos

Hibernate Core Contém os serviços básicos Metadados escritos em XML Consultas

HQL: Hibernate Query Language Interfaces utilizando critérios e exemplos

Não depende de outros módulos Não depende de uma versão específica do JDK Executável em qualquer servidor Web e/ou de

Aplicação e também em aplicações desktop

Módulos

Hibernate Annotations Permite a escrita de metadados através de Annotations Beneficia-se da tipagem do Java Compatível com refatorações de código Semântica familiar para quem já está acostumado com

metadados em XML Utiliza as Annotations da especificação JPA Possui Annotations próprias para configurações

avançadas não presentes na especificação Necessita do JDK 5.0

Módulos

Hibernate Entity Manager Implementação da especificação JPA Permite a escrita de código compatível com qualquer

framework de persistência que implemente a especificação

Utiliza o pacote javax.persistence Não disponibiliza todas as funcionalidades do

Hibernate

Separação em camadas

É uma boa prática dividir aplicações de médio e grande porte em camadas Padrão Layers (Pattern-Oriented Software

architecture) Divisão mais utilizada

Apresentação Negócio Persistência

Separação em camadas

Permite a especificação de intefaces para os diversos tipos de serviços A implementação pode ser mudada sem afetar

significativamente o código de outras camadas A comunicação ocorre das camadas superiores

para as camadas inferiores Uma camada só depende da camada

imediatamente inferior Camada de apresentação não sabe da existência da

camada de persistência

Criando um projeto

Next >....

Finish

Adicionando as bibliotecas

Criando uma entidade@Entity@Table(name = "MESSAGES")public class Message {

@Id@GeneratedValue@Column(name = "MESSAGE_ID")private Long id;

@Column(name = "MESSAGE_TEXT")private String text;

@ManyToOne(cascade = CascadeType.ALL)@JoinColumn(name = "NEXT_MESSAGE_ID")private Message nextMessage;

private Message() {}

public Message(String text) {this.text = text; }

//gets e sets}

Annotations da classe

Entity Informa que a classe pode ser persistida pelo

Hibernate Table

Informa o nome da tabela em que os objetos da classe devem ser armazenados

@Entity@Table(name = "MESSAGES")public class Message {

Construtores

Obrigatório um construtor sem argumentos

private Message() {}

public Message(String text) {

this.text = text; }

Propriedades

Representa o identificador do objeto Preenchido automaticamente quando o objeto é

salvo Mapeado para a chave primária da tabela Se duas instâncias tiverem o mesmo

identificador, elas representam a mesma linha da tabela

@Id@GeneratedValue@Column(name =

"MESSAGE_ID")private Long id;

Atributos

O atributo text é armazenado na coluna MESSAGE_TEXT

@Column(name = "MESSAGE_TEXT")

private String text;

O atributo nextMessage é uma associação many-to-one

Mapeado pela chave estrangeira NEXT_MESSAGE_ID

@ManyToOne(cascade = CascadeType.ALL)

@JoinColumn(name = "NEXT_MESSAGE_ID")

private Message nextMessage;

Transparência Não há a necessidade de se herdar de uma

superclasse ou de implementar um interface POJO - Plain Ordinary Java Objects

Classes podem ser reutilizadas fora do contexto de persistência Interface com o usuário Testes de unidades

As entidades não precisam nem saber que serão persistidas

Grande nível de portabilidade

Configurando o Hibernate Configuração feita através do arquivo

hibernate.cfg.xml Deve estar localizado na raiz do classpath

Localização default Para projetos maven, utilizar a pasta

src/main/resources Configurações contém

Parâmetros de acesso a base de dados Pool de conexões Entidades a serem persistidas

hibernate.cfg.xml<hibernate-configuration>

<session-factory>

<!-- Parâmetros de acesso a base de dados -->

<property name="connection.driver_class">org.postgresql.Driver

</property><property name="connection.url">

jdbc:postgresql://localhost:5432/curso</property>

<property name="connection.username">postgresql</property>

<property name="connection.password">postgresql</property>

<property name="dialect">org.hibernate.dialect.PostgreSQLDialect

</property>

...

hibernate.cfg.xml...<!-- Configuração do Pool de conexões --><property name="c3p0.min_size">5</property><property name="c3p0.max_size">20</property><property name="c3p0.timeout">300</property><property name="c3p0.max_statements">50</property><property name="c3p0.idle_test_period">3000</property>

<!-- Exibe no console o SQL gerado pelo hibernate--><property name="show_sql">true</property>

<!-- Cria e executa a DDL (tabelas, colunas, etc...)-->

<property name="hbm2ddl.auto">create</property>

<!-- Informa as Entidades da aplicação --><mappingclass="br.gov.serpro.curso.hibernate.exemplo1.Message" />

</session-factory></hibernate-configuration>

Armazenando uma Mensagem

//Obtendo o SessionSession session = HibernateUtil.getSessionFactory().openSession();

//Iniciando a transaçãoTransaction tx = session.beginTransaction();

// Criando uma mensagemMessage message = new Message("Hello World");

// Salvando a MensagemLong msgId = (Long) session.save(message);

//Fazendo o commit da transaçãotx.commit();

//Fechando o Sessionsession.close();HibernateUtil.shutdown();

Objetos utilizados

Session Contém métodos utilizados para armazenar e

recuperar entidades Armazena uma lista de comandos SQL que serão

executados, em algum momento, na base de dados Mantém as entidades gerenciadas pelo Hibernate

Recuperadas, inseridas ou atualizadas através do Session Único para cada thread

Objetos utilizados

Transaction Utilizado para delimitar as transações com a base de

dados Geralmente gerenciado pelo container

EJB Spring

SessionFactory Utilizado para criar os Session Reutilizável por toda a aplicação

HibernateUtil

public class HibernateUtil {

private static SessionFactory sessionFactory;

static {sessionFactory = new

AnnotationConfiguration().configure().buildSessionFactory();

}

public static SessionFactory getSessionFactory() {return sessionFactory;

}

public static void shutdown() {getSessionFactory().close();

}}

Recuperando as Mensagens

Session newSession = HibernateUtil.getSessionFactory().openSession();Transaction newTransaction = newSession.beginTransaction();

//Criando e executando uma consultaQuery query = newSession

.createQuery("from Message m order by m.text asc");List<Message> messages = query.list();

//Retornando a quantidade de mensagens encontradasSystem.out.println(messages.size() + " message(s) found:");

//Retornando as mensagens encontradasfor (Message msg : messages) {

System.out.println(msg.getText());}

newTransaction.commit();newSession.close();HibernateUtil.shutdown();

Objetos Utilizados

Query Cria as consultas Associa os parâmetros Executa as consultas

Dirty Checking e CascadeSession thirdSession =

HibernateUtil.getSessionFactory().openSession();Transaction thirdTransaction = thirdSession.beginTransaction();

// recuperando a mensagemmessage = (Message) thirdSession.get(Message.class, msgId);

//Alterando o textomessage.setText("Greetings Earthling");

//Criando uma nova mensagem e associando à antigamessage.setNextMessage(new Message("Take me to your leader"));

thirdTransaction.commit();thirdSession.close();HibernateUtil.shutdown();

Dirty Checking e Cascade

Dirty Checking O Hibernate automaticamente verifica quando um atributo

é modificado O atributo é atualizado na base de dados sem a necessidade

de uma chamada explicita É possível somente dentro de uma transação e para objetos

gerenciados pelo Session Cascade

Novas entidades são persistidas automaticamente Devem ser alcançáveis a partir de uma entidade gerenciada

Metadados

Utilizados para especificar o mapeamento entre Classes e tabelas Propriedades e colunas Associações e chaves estrangeiras Tipos de atributos Java e tipos de atributos SQL

Pode ser escrita de duas formas Arquivos XML Xdoclet Java Annotations

Porque não usar XML

Pode ser tornar pouco legível e de difícil edição Requer maior digitação Falta de valores default para atributos e elementos

Não necessariamente mais flexível e mais fácil de manter do que código Java

Mais fácil encontrar um bom editor Java do que um bom editor de XML

Metadados com Annotations

A meta-informação fica perto dos dados que ela descreve

Compatível com refatoração de código Renomear, deletar e remover classes e propriedades

Sem necessidade de fazer parser em XML Inicialização mais rápida

Lido através de reflection na inicialização do Hibernate

Disponível a partir da JDK 5.0

Mapeando Propriedades

Ao mapear uma classe através da Annotation @Entity, todas as propriedades serão consideradas persistentes

Propriedades não persistentes devem receber a Annotation @Transient ou o modificador transient

Por default, o nome da coluna será o mesmo da propriedade

Meio de acesso é o mesmo do @ID Definido na propriedade ou no método get()

@Column

Aplicável para propriedades simples Atributos

name Nome da coluna a qual a propriedade é mapeada

unique Se o valor deve ser único ou não

nullable Se a propriedade pode ser nula ou não

Mapeando Propriedades: Tipos

Mapeando Propriedades: Tipos

Datas

Propriedades podem ser do tipo java.util.Date ou java.util.Calendar

Precisão default é TIMESTAMP Precisão pode ser alterada através da Annotation

@Temporal

@Temporal(TemporalType.DATE)private Date date1;

@Temporal(TemporalType.TIME)private Date date2;

@Temporal(TemporalType.TIMESTAMP)private Date date3;

Identidade das Entidades Identidade versus Equivalência em Java

Identidade Operador == Definido pela máquina virtual Dois objetos são idênticos se eles apontam para o mesmo

local da memória Equivalência

Definida pelas classes que sobreescrevem o método equals()

Dois objetos diferentes possuem o mesmo valor Dois objetos String podem ter a mesma seqüência de

caracteres

Identidade das Entidades

Identidade na base de dados Duas Entidades são idênticas se elas representam a

mesma linha na base de dados Se são armazenadas na mesma tabela e possuem a mesma

chave primária

Adicionando um Identificador

O valor é gerado automaticamente pelo Hibernate Não deve ser mudado pela aplicação

@Entity@Table(name="CATEGORY")public class Category {

@Id@GeneratedValue(strategy = GenerationType.AUTO)@Column(name = "CATEGORY_ID")private Long id;

public Long getId() { return id; }}

Escolhendo a chave primária

Uma chave candidata é uma coluna, ou um conjunto de colunas, que pode ser utilizada para identificar um registro na base de dados

Requisitos O valor nunca deve ser nulo Cada registro deve ter um valor único O valor nunca deve mudar

Escolhendo a chave primária

Chaves naturais são aquelas que possuem significado para o domínio da aplicação CPF de um usuário Seqüencial gerado pela aplicação

Dificilmente chaves naturais atendem os requisitos necessários

Escolhendo a chave primária

Chaves cegas Melhor alternativa Não possuem significado para o domínio da aplicação Geradas pelo banco de dados ou pela aplicação

Geração automática de chaves

Hibernate suporta diversas estratégias para geração automática de chaves

A estratégia deve ser especificada através da Annotations @GeneratedValue

Estratégias

IDENTITY Suporte para colunas IDENTITY nos bancos de dados

DB2, MySQL, MS SQL Server, Sybase e HypersonicSQL

Os tipos devem ser long, short ou int SEQUENCE

Utiliza um Sequence para a geração de chaves Suporte para DB2, PostgreSQL e Oracle dentre outros Nome default da sequence é hibernate_sequence

Estratégias

increment Na inicialização o Hibernate lê qual o maior valor

para a chave da tabela O valor é atribuído e incrementado em cada inserção Não deve ser utilizado se o Hibernate não tiver acesso

exclusivo para a base de dados

Estratégias

AUTO Escolhe entre as estratégias IDENTITY, SEQUENCE

ou HILO Estratégia escolhida depende do banco de dados

utilizado Utilizado para manter a portabilidade

Outras hilo, seqhilo, uuid.hex, guid, select IdentifierGenerator

Permite a implementação de sua própria estratégia

Mapeando Componentes

Entidades são classes persistentes que representam objetos do domínio da aplicação Possuem identidade própria

Hibernate suporta a construção de um modelo de objetos de alta granularidade Propriedades de uma Entidade podem ser

encapsuladas em outros objetos Tais objetos não possuem identidade própria Chamados de Objetos Valores ou Componentes

Mais classes do que tabelas

Mapeando Componentes

Mapeando Componentes

User Representa uma Entidade Possui identidade própria

Chave primária na base de dados Uma referência para User é persistida como uma

chave estrangeira Tem seu próprio ciclo de vida

Existe independentemente de qualquer outra entidade

Mapeando Componentes

Address Representa um Componente Não possui identidade própria Pertence a Entidade User Estado é persistido no registro referente ao User a

qual está associado Ciclo de vida dependente de User Se dois Usuários morarem no mesmo apartamento,

cada um tem uma referência para um objeto distinto Comportamento similar a tipos como String e Integer

Mapeando Componentes

@Embeddablepublic class Address {

@Column(name = "ADDRESS_STREET", nullable = false)private String street;

@Column(name = "ADDRESS_CITY", nullable = false)private String city;

//gets e sets...}

Mapeando Componentes

@Entity@Table(name = "USERS")public class User {

@Id@GeneratedValue(strategy=GenerationType.AUTO)private Long id;

private String name;

private String email;

private Address address;

//gets e sets}

Mapeando Herança

A utilização de uma tabela para cada entidade não funciona tão bem para heranças entre entidades

Bancos de dados SQL, de maneira geral, não possuem o conceito de herança Soluções proprietária podem comprometer a

portabilidade da aplicação

Estratégias

Tabela por classe concreta Tabela por classe concreta, utilizando junção Tabela por hierarquia Tabela por classe (concreta ou não)

Uma tabela por Classe Concreta: Modelagem

Uma tabela por Classe Concreta

Estratégia mais simples Todas propriedades de uma classe, incluindo as

herdadas, são mapeadas para a mesma tabela Mapeamento convencional pode ser utilizado Não suporta associações polimórficas

adequadamente Associação para classe abstrata necessitaria que a

chave estrangeira referenciasse duas tabelas

Uma tabela por Classe Concreta

Diferentes colunas em diferentes tabelas passariam a possuir a mesma semântica

Recomendável somente para a hierarquia superior Associações polimórficas geralmente não necessárias

e não recomendadas Modificações na superclasse são muito raras

select CREDIT_CARD_ID, OWNER, NUMBER, EXP_MONTH, EXP_YEAR ...from CREDIT_CARD

select BANK_ACCOUNT_ID, OWNER, ACCOUNT, BANKNAME, ...from BANK_ACCOUNT

Mapeando a Superclasse

@MappedSuperclasspublic abstract class BillingDetails {

@Column(name = "OWNER", nullable = false)private String owner;

//gets e sets...}

Mapear a super classe através da Annotattion @MappedSuperclass

Mapeando as Subclasses

Nenhuma configuração extra é necessária

@Entitypublic class BankAccount extends BillingDetails{

@Id@GeneratedValueprivate Long id;

private Integer account;

private String bank;}

Mapeando as Subclasses

Mapeamento das propriedades herdadas pode ser alterado

Adicionar a Annotation @AttributeOverride

@Entity@AttributeOverride(name = "owner", column = @Column(name =

"CC_OWNER", nullable = false))public class CreditCard extends BillingDetails {

@Id@GeneratedValueprivate Long id;

private String number; }

Uma tabela por Classe Concreta: Union

Com a utilização de consultas com Union, alguns problemas podem ser eliminados

Suporte a associações polimórficas Hibernate utiliza o Union para simular uma única

tabela As subclasses herdam o Id da super-classe

Mapeando a Super Classe

@Entity@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)public class BillingDetails {

@Id @GeneratedValue@Column(name = "BILLING_DETAILS_ID")private Long id = null;

@Column(name = "OWNER", nullable = false)private String owner;

}

É utilizada a Annotation @Inheritance

Mapeando as Subclasses

@Entitypublic class CreditCard extends BillingDetails{

@Column(name = "CC_NUMBER")private String number;

}

Não é necessário mapear o identificador O Identificador é mapeado na super-classe

Tabela por Hierarquia

Tabela por Hierarquia

Toda uma hierarquia é mapeada em uma única tabela

A tabela possui colunas para todas as propriedades de todas as entidades da hierarquia

A subclasse de cada registro é identificada através de uma coluna discriminatória

Vantagem Possui a melhor performance

Tabela por Hierarquia

Desvantagens Valores para colunas das subclasses devem sempre

permitir NULL Perca da restrição NOT NULL

Dados são desnormalizados

Mapeando a Superclasse

@Entity@Inheritance(strategy = InheritanceType.SINGLE_TABLE)public abstract class BillingDetails {

@Id @GeneratedValue@Column(name = "BILLING_DETAILS_ID")private Long id = null;

@Column(name = "OWNER", nullable = false)private String owner;

}

O valor do atributo strategy deve ser mudado para InheritanceType.SINGLE_TABLE

Tabela por classe

Tabela por classe

Uma tabela para todas as classes, incluindo as abstratas, que possuem propriedades para serem persistidas

Relação de herança é representada por uma chave estrangeira

As tabelas não contém colunas para as propriedades herdadas

A chave primária é também uma chave estrangeira para a super classe

Tabela por classe

Vantagens Os dados são normalizados Restrições podem ser mantidas

Desvantagens Performance pode ser muito baixa para hierarquias

complexas Necessidade de muitas junções

Mapeando a Superclasse

@Entity@Inheritance(strategy = InheritanceType.JOINED)public abstract class BillingDetails {

@Id @GeneratedValue@Column(name = "BILLING_DETAILS_ID")private Long id = null;

@Column(name = "OWNER", nullable = false)private String owner;

}

O valor do atributo strategy deve ser mudado para InheritanceType.JOINED

Mapeando as Subclasses

@Entitypublic class CreditCard extends BillingDetails{

@Column(name = "CC_NUMBER")private String number;

}

Id é mapeado na super-classe

Escolhendo a melhor estratégia

Tabela por classe concreta, utilizando junção Se associações ou consultas polimórficas nunca ou

raramente são utilizadas Tabela por hierarquia

Se associações ou consultas polimórficas são utilizadas e

Se as subclasses declaram poucas propriedades Maior diferença entre as subclasses é o comportamento

Escolhendo a Melhor Estratégia

Tabela por Classe Se associações ou consultas polimórficas são

utilizadas e Se as subclasses declaram muitas propriedades

Maior diferença entre as subclasses são os dados que elas armazenam

Se a largura e o comprimento da hierarquia não for muito grande

Mapeando coleções

Objetos podem possuir coleções As coleções podem ter duas naturezas

Coleções de valores Tipos primitivos Componentes

Coleções de entidades

Coleção de tipos primitivos: Set

Um Item possui um conjunto de imagens Somente o nome da imagem é persistido Nomes não devem ser duplicados para um

determinado Item

Annotations

@CollectionOfElements Utilizada para coleções de tipos primitivos ou para

coleções de objetos valores @JoinTable

Informa a tabela em que a coleção é persistida Atributos

Name Nome da tabela

JoinColumns Coluna que é chave estrangeira Chave estrangeira referencia a tabela da entidade que possui a coleção

Annotations

@JoinColumn É o tipo do atributo JoinColumns da Annotation

@JoinTable Annotation dentro de outra Annotation

Mapeia a coluna utilizada para junções Atributos

Name Nome da coluna

Classe Item

@Entitypublic class Item {

@Id @GeneratedValue@Column(name = "ITEM_ID")private Long id;

private String name;

@CollectionOfElements@JoinTable(name = "ITEM_IMAGE", joinColumns = @JoinColumn(name

= "ITEM_ID"))@Column(name = "FILENAME", nullable = false)private Set<String> images = new HashSet<String>();

}

Coleção de tipos primitivos: List

Caso a aplicação necessite guardar a ordem dos elementos

Necessário uma coluna para informar a posição de cada elemento

Classe Item

@Entitypublic class Item {...

@CollectionOfElements@JoinTable(name = "ITEM_IMAGE", joinColumns = @JoinColumn(name = "ITEM_ID"))@Column(name = "FILENAME", nullable = false)@IndexColumn(name = "POSITION")private List<String> images = new ArrayList<String>();

}

Deve-se adicionar a Annotation @IndexColumn

Coleção de tipos primitivos: Map

Se o Item possuir um Map de imagens É necessário guardar o elemento e sua chave

Classe Item

@Entitypublic class Item {...

@CollectionOfElements@JoinTable(name = "ITEM_IMAGE", joinColumns =

@JoinColumn(name = "ITEM_ID"))@Column(name = "FILENAME", nullable = false)@MapKey(columns = @Column(name = "IMAGENAME"))private Map<String, String> images = new HashMap<String,

String>();}

Utilizar a Annotation @MapKey Atributo columns informa o nome da coluna que

armazena a chave

Associação entre Entidades

Podem ser de diversas multiplicidades One-to-many One-to-one Many-to-many Many-to-one

Também são implementadas através de atributos Para associações one-to-many e many-to-many é

necessário a utilização de coleções

Implementando associações

Coleções devem ser acessadas através de suas Interfaces Set, ao invés de HashSet List, ao invés de ArrayList

Associações Bidirecionais

Os dois lados da associação devem estar consistentes Necessário mesmo para aplicações que não usam

Hibernate

Classe Category

public class Category {

private String name;

private Category parentCategory;

private Set<Category> childCategories = new HashSet<Category>();

//gets e sets//...

}

Mantendo a Consistência

Category aParent = new Category();Category aChild = new Category();

aChild.setParentCategory(aParent);aParent.getChildCategories().add(aChild);

Relacionamento Pai-Filho

Classe Bid

Relaciona-se a somente um Item Mapeamento realizado através da Annotation

@ManyToOne Annotation @JoinColumn informa qual a chave

estrangeira Atributo name

Nome da coluna Atributo nullable

Se o relacionamento é opcional ou não

Classe Bid

@Entitypublic class Bid {

@Id@GeneratedValue@Column(name = "BID_ID")private Long id;

private Integer amount;

@ManyToOne@JoinColumn(name = "ITEM_ID", nullable = false)private Item item;

}

Relacionamento Bidirecional

O relacionamento pode ou não ser declarado como bidirecional

Para relacionamentos bidirecionais o outro lado da associação é mapeado com a Annotation inversa @OneToMany no caso de Item

Classe Bid utiliza a Annotation @ManyToOne

Relacionamento Bidirecional

@Entitypublic class Item {

@Id@GeneratedValue@Column(name = "ITEM_ID")private Long id;

private String name;

@OneToMany(mappedBy = "item")private Set<Bid> bids = new HashSet<Bid>();

}

Relacionamento Bidirecional

@OneToMany(mappedBy = "item") Detalhes do mapeamento são configurados no outro lado

da associação Através da Annotation @ManyToOne

Quando as referências de uma associação mudam, as duas propriedades são alteradas bid.setItem(item); item.getBids().add(bid);

Relacionamento Bidirecional

Na base de dados somente uma atualização precisa ser feita Atualização da chave estrangeira Hibernate não precisa executar dois comandos

UPDATE Uma das alterações é sincronizada e a outra é

ignorada Não há efeito colateral se as associações estiverem

consistentes

Relacionamento Bidirecional

O lado que contém os detalhes do mapeamento é o lado que é sincronizado Relacionamentos many-to-one ou one-to-many

Lado Many é sincronizado Relacionamento one-to-one

Lado que possuir a chave estrangeira é sincronizado Relacionamento many-to-many

Lado que mapear a tabela de junção é sincronizado Os lados do relacionamento não sincronizados

possuem o atributo mappedBy na Annotation

Relacionamento Bidirecional

Bid bid = new Bid();bid.setAmount(Integer.valueOf(1000));

Item item = new Item();item.setName("computador");

bid.setItem(item);item.getBids().add(bid);

Session session = HibernateUtil.getSessionFactory().openSession();Transaction transaction = session.beginTransaction();

//O item tem que ser salvo primeirosession.save(item);session.save(bid);

Relacionamento One-to-One

One-to-One

O atributo deve conter a Annotation @OneToOne Se o relacionamento for bidirecional os dois

atributos devem conter a Annotation Em uma das propriedades deve ser adicionada a

Annotation @JoinColumn Na outra propriedade deve ser adicionado o

atributo mappedBy à Annotation @OneToOne

Classe User

@Entity@Table(name = "USERS")public class User {

@Id@GeneratedValue(strategy=GenerationType.AUTO)private Long id;

private String name;

@OneToOne@JoinColumn(name = "SHIPPING_ADDRESS_ID")private Address address;

}

Classe Address

@Entitypublic class Address {

@Id@GeneratedValueprivate Long id;

@Column(nullable = false)private String street;

@OneToOne(mappedBy = "address")private User user;

}

Ralacionamento Many-to-Many

Many-to-Many

O atributo deve conter a Annotation @ManyToMany

Se o relacionamento for bidirecional os dois atributos devem conter a Annotation

Em uma das propriedades deve ser adicionada a Annotation @JoinTable

Na outra propriedade deve ser adicionado o atributo mappedBy à Annotation @ManyToMany

@JoinTable

Contém o nome da tabela de relacionamento O atributo joinColumns mapeia a coluna

associada à entidade mapeada O tipo do atributo é @JoinColumn

Annotation dentro de annotation O atributo inverseJoinColumns mapeia a coluna

associada à outra entidade O tipo do atributo também é @JoinColumn

Classe Category

@Entitypublic class Category {

@Id@GeneratedValue@Column(name = "CATEGORY_ID")private Long id;

private String name;

@ManyToMany@JoinTable(name = "CATEGORY_ITEM", joinColumns = @JoinColumn(name

= "CATEGORY_ID"), inverseJoinColumns = @JoinColumn(name = "ITEM_ID"))private Set<Item> items = new HashSet<Item>();

}

Classe Item

@Entitypublic class Item {

@Id@GeneratedValue@Column(name = "ITEM_ID")private Long id;

private String name;

@ManyToMany(mappedBy = "items")private Set<Category> categories = new

HashSet<Category>();}

Polimorfismo e LazyLoading

Utilizando-se polimorfismo, as entidades são referenciadas através de suas super classes

Não se deve fazer o cast da referência para alguma das subclasses, mesmo que se saiba o tipo do objeto

Para associações carregadas por LazyLoading, é utilizado um objeto proxy

O objeto proxy não pode ser atribuído a uma subclasse da classe referenciada

Associações: Polimorfismo

Entidade User

@Entity@Table(name = "USERS")public class User {

@Id@GeneratedValueprivate Long id;

@OneToOne(fetch = FetchType.LAZY)@JoinColumn(name = "DEFAULT_BILLING_ID")private BillingDetails defaultBilling; }

Polimorfismo e LazyLoading

User user = (User) session.get(User.class, userId);BillingDetails bd = user.getDefaultBilling();

CreditCard cc = (CreditCard) session.load(CreditCard.class, bd.getId());System.out.println(cc.getNumber());

User user = (User) session.get(User.class, userId);BillingDetails billingDetails = user.getDefaultBilling();

//imprime falseSystem.out.println(billingDetails instanceof CreditCard);

//ClassCastExceptionCreditCard creditCard = (CreditCard) billingDetails;

Ciclo de Vida dos Objetos

Em seu ciclo de vida, uma entidade pode assumir diversos estados relativos a sua persistência Transiente Persistente Desconectada Removida

Entidades devem se comportar de maneira apropriada mesmo que ainda não estejam persistentes

Ciclo de Vida dos Objetos

O ciclo de vida de uma entidade pode ser afetado através de chamadas às interfaces do Hibernate Operações de alterações e consultas na base de dados Operações para delimitar a existência de uma

transação e de um Sesssion Para o correta implementação, a aplicação deve

se preocupar com o estado e o ciclo de vida do objeto

Ciclo de Vida dos Objetos: Conceitos Unidade de trabalho

Conjunto de operações, geralmente atômicas Contexto de persistência

Cache de todas alterações feitas em entidades em uma única unidade de trabalho

Estado Transiente

É o estado que um objeto assume quando é criado através do operador new

Não está associado a nenhum registro da base de dados

Os dados são perdidos quando o objeto não é mais referenciado e torna-se disponível para o Coletor de Lixo

Não são gerenciados pelo Hibernate Não possuem comportamento transacional

Estado Persistente

É uma entidade com um identificador na base de dados Possui uma chave primária

São objetos gerenciados pelo Hibernate Podem ser objetos criados pela aplicação

Tornam-se persistentes através de chamadas ao Hibernate

Tornam-se persistentes se passarem a ser referenciados por outros objetos persistentes

Depende do tipo de Cascade

Persistente

Podem ser objetos retornados da base de dados Através de uma consulta Através de uma associação entre um objeto persistente

Podem ser objetos atualizados na base de dados São sempre associados a um Contexto de

Persistência São guardados em um cache Hibernate identifica qualquer alteração realizado no

objeto

Removido

Um objeto pode ser removido de duas maneiras Uma chamada explicita ao Hibernate Através de uma operação com Cascade

Um objeto neste estado é removido ao final da Unidade de Trabalho É gerenciado pelo Contexto de Persistência até que a

Unidade de Trabalho termine

Desconectado

Objetos Persistentes passam para o estado Desconectado quando a Unidade de Trabalho é completada e o Contexto de Persistência é encerrado

O objeto não é mais sincronizado com a base de dados

Os dados do objeto podem se tornar desatualizados Seus dados ainda podem ser alterados pela aplicação Podem voltar ao estado Persistente

Contexto de Persistência

Mantém um cache de instâncias gerenciadas Entidades no estado Persistente e Removido

Conceito abstrato Não representado por um único objeto

Cada objeto Session possui um Contexto de Persistência

Permite Dirty Checking automático Delimita um escopo para a identidade das Entidades

Não possui instâncias duplicadas

Dirty Checking Automático É possível através do Contexto de Persistência O dados de uma entidade são sincronizados com

a base de dados No final da unidade de trabalho Em outros momentos: antes de uma consulta

Somente os objetos alterados são atualizados na base de dados

Atualização é retardada o máximo possível e é feita de forma automática Tempo de Lock do banco de dados é reduzido

Cache de Primeiro Nível

Armazenado no Contexto de Persistência Mantém todas as instâncias de uma Unidade de

Trabalho Provê ganho de performance para a aplicação Torna possível Repeatable Read

Ao buscar mais de uma vez uma mesma entidade, os dados retornados são os mesmos

O escopo é restrito à thread de execução

Cache de Primeiro Nível

Um único objeto representa um determinado registro na base de dados Evita alterações conflitantes

Contexto Persistência: Escopo

Algumas funcionalidades da aplicação necessitam de diversas requisições para serem completadas

O escopo do Contexto de Persistência pode, ou não, ser o mesmo da funcionalidade

Duas estratégias são as mais utilizadas para delimitar o escopo do Contexto de Persistência Um objeto Session por requisição Unidade de Trabalho de longa duração

Uma Session por Requisição

Quando o servidor recebe alguma requisição que requer acesso a base de dados uma nova Unidade de Trabalho é iniciada

A Unidade de Trabalho é encerrada quando a requisição é processada e a resposta está pronta para ser enviada ao usuário

As entidades são mantidas em estado Desconectado entre uma requisição e outra

Uma Session por Requisição

Entidades mantidas entre duas requisições necessitam ser reconectadas para acessar serviços de persistência novamente

Qualquer alteração feita em entidades desconectadas deve ser sincronizada manualmente Operações de atualização e/ou merge

Objetos Desconectados

Unidade de Trabalho de Longa Duração

Objeto Session é mantido aberto durante várias requisições O Contexto de Persistência se propaga durante toda a

conversação Conexões com a base de dados não são mantidas

abertas A cada nova requisição o contexto é reconectado à base

de dados As entidades são mantidas no estado Persistente

Não requer merge ou atualização manual

Unidade de Trabalho de Longa Duração

No final da conversação, o Contexto é sincronizado com a base de dados e encerrado

A atomicidade das alterações é relativa à funcionalidade como um todo

Contexto expandido

Escopo limitado ao Contexto de Persistência

Session session = HibernateUtil.getSessionFactory().openSession();Transaction transaction = session.beginTransaction();

Long id = (Long) session.save(new Message("Hibernate"));

Message messageB = (Message) session.get(Message.class, id);Message messageC = (Message) session.get(Message.class, id);

System.out.println(messageB.equals(messageC));

transaction.commit();session.close();

Session session2 = HibernateUtil.getSessionFactory().openSession();Transaction transaction2 = session2.beginTransaction();

Message messageD = (Message) session2.get(Message.class, id);System.out.println(messageC.equals(messageD));

Identidade de objetos Desconectados

Sempre que se for trabalhar com objetos desconectados, deve-se sobrescrever o método equals() Para objetos desconectados a garantia de uma única

instância por registro é perdida Implementação default não garante o comportamento

desejado Mesmo que dois objetos possuam todas as

propriedades iguais, o método pode retornar false

equals() Deve retornar verdadeiro se duas instâncias

corresponderem ao mesmo registro Quais propriedades devem ser comparadas? Somente a chave primária

Problema: entidades transientes não possuem valores para a chave primária

Todas as propriedades Problema: entidades referentes ao mesmo registro não

são consideradas iguais se alguma propriedade mudar Melhor abordagem: comparar a chave de negócio

Chave de negócio

Uma propriedade, ou um conjunto delas, que é único para cada registro Chave candidata

Não precisa ser imutável, basta que mude com pouca freqüência

Implementaçãopublic boolean equals(Object other) {

if (!(other instanceof User)){return false;

}User that = (User) other;return this.name.equals(that.getName());

}

public int hashCode() {return this.name.hashCode();

}

Interagindo com o Hibernate

O Hibernate provê diversos serviços Operações CRUD

Criar Recuperar Atualizar Deletar

Realização de consultas Controle de transação Gerenciamento do Contexto de Persistência

Interagindo com o Hibernate

Os serviços são disponibilizados através de diversas Interfaces Session Transaction Query Criteria

Armazenando e Recuperando Objetos

Para armazenar ou recuperar objetos, é necessário iniciar uma Unidade de Trabalho

Session session = sessionFactory.openSession();Transaction transaction = session.beginTransaction();

Um Contexto de Persistência é criado Irá gerenciar todas as entidades do Session

Uma transação também deve ser iniciada mesmo para operações de leitura

Armazenando um Objeto

// Quando é instanciado, o item está no estado TransienteItem item = new Item();item.setName("Item 1");

// um novo Session é abertoSession session = HibernateUtil.getSessionFactory().openSession();Transaction transaction = session.beginTransaction();

// O item passa para o estado Persistente e fica associado//ao Session e ao Contexto de Persistênciasession.save(item);

//As mundanças são sincronizadas com a base de dadostransaction.commit();

//O Session é fechado e o Contexto de Persistência encerrado.//O item passa para o estado Desconectadosession.close();

Armazenando um Objeto

Recuperando uma Entidade

Session session = HibernateUtil.getSessionFactory().openSession();Transaction tx = session.beginTransaction();

//Item é recuperado no estado TransienteItem item = (Item) session.load(Item.class, new Long(1234));// Item item = (Item) session.get(Item.class, new Long(1234));

tx.commit();

//Item passa para o estado Desconectadosession.close();

Recuperando uma Entidade

Recuperando uma Entidade

Método get() Se o objeto procurado não for encontrado, retorna null

Método load() Se o objeto procurado não for encontrado, lança a

exceção ObjectNotFoundException A existência do objeto só é verificada quando alguma

propriedade é acessada

Modificando um Objeto Persistente

Todo objeto retornado pelos métodos load() ou get() ou através de uma consulta, assume o estado Persistente

Os objetos podem ser modificados e as alterações são sincronizadas com a base de dados

Modificando um Objeto Persistente

Long id = armazenarItem();

Session session = HibernateUtil.getSessionFactory().openSession();Transaction tx = session.beginTransaction();

Item item = (Item) session.get(Item.class, id);item.setName("Playstation");

//Dados sincronizados com a base de dados. Dirty Cheking automáticotx.commit();session.close();

Modificando um Objeto Persistente

De Persistente para Transiente

Long id = armazenarItem();

Session session = HibernateUtil.getSessionFactory().openSession();Transaction tx = session.beginTransaction();

//Item no estado PersistenteItem item = (Item) session.load(Item.class, id);

//Item no estado Removidosession.delete(item);

tx.commit();

//Item no estado Transientesession.close();

De Persistente para Transiente

Trabalhando com Objetos Desconectados

Modificações realizadas em objetos Desconectados não são sincronizadas na base de dados

A atualização da base de dados pode ser feita de duas formas Reconectando o objeto – reattach Realizando o merge do objeto

Reconectando um Objeto

Um objeto é reconectado através do método update() da interface Session

O objeto passa a ser gerenciado pelo Session e assume o estado Persistente

Deve-se ter certeza de que o Session ainda não possui um objeto gerenciado com o mesmo Id Somente uma instância do Contexto de Persistência

pode estar associada a um determinado registro

Reconectando um Objeto

Item item = recuperarItem(armazenarItem());

item.setName("Novo nome");

Session session = HibernateUtil.getSessionFactory().openSession();Transaction transaction = session.beginTransaction();

session.update(item);

item.setPrice(Integer.valueOf(114));

transaction.commit();session.close();

Reconectando um Objeto

NonUniqueObjectException

Long id = armazenarItem();Item item = recuperarItem(id);

item.setName("Novo nome");

Session session = HibernateUtil.getSessionFactory().openSession();Transaction transaction = session.beginTransaction();

Item item2 = (Item) session.get(Item.class, id);session.update(item);

item.setPrice(Integer.valueOf(114));

transaction.commit();session.close();

Realizando o merge() de um Objeto

O merge de um objeto é feito através do método merge() da Interface Session

A a base de dados é atualizada com os valores contidos no objeto

Os dados do objeto também atualizam o objeto persistente que possuir o mesmo identificador Se não existir um objeto persistente associado ao

Session, ele é carregado

Método merge()

O objeto desconectado não passa a ser associado ao Session Continua desconectado

O objeto persistente é retornado

Método merge()

Long id = armazenarItem();Item item = recuperarItem(id);

item.setName("Novo nome!!!");

Session session = HibernateUtil.getSessionFactory().openSession();Transaction transaction = session.beginTransaction();

Item item2 = (Item) session.get(Item.class, id);Item item3 = (Item) session.merge(item);

transaction.commit();session.close();

Método merge()

Persistência Transitiva

Em aplicações não triviais, diversas Entidades podem estar associadas

A aplicação deve poder manipular as associações Uma rede de objetos pode consistir de objetos em

diferentes estados Persistentes Desconectados Transientes

Persistência Transitiva A Persistência Transitiva permite persistir objetos

Desconectados e Transientes automaticamente Um objeto Transiente associado a um Persistente,

torna-se persistente automaticamente Não há necessidade de chamar o método save()

Diversas operações poder ser cascateadas Salvar Deletar Reconectar (update) Merge

Cascade

Por default, em nenhuma operação é feito o cascade O Hibernate permite a configuração do tipo de

comportamento desejado para cada associação Todos os tipos de associações podem ser

configuradas one-to-one one-many many-to-many many-to-one

org.hibernate.annotations.CascadeType

SAVE_UPDATE Objetos transientes são salvos Objetos desconectados são sincronizados

DELETE Objetos relacionados são deletados após a chamada

do método delete() ALL

Habilita todos os tipos de cascade

org.hibernate.annotations.CascadeType

DELETE_ORPHAN Deleta um objeto se ele for excluído da coleção Aplicável somente para relacionamentos One-to-

Many Cascade é aplicável somente para

relacionamentos entre entidades

Exercício

Consultas

Umas das partes mais interessantes do acesso a dados Consultas complexas podem levar um bom tempo

para serem escritas e podem ter considerável impacto na performance da aplicação

Consultas são escritas utilizando conceitos de orientação a objetos Objetos no lugar de tabelas Propriedades no lugar de colunas

Experiência em SQL não é desprezada

Consultas

Podem ser feitas de três maneiras Hibernate Query Language (HQL) Criteria API e Query by Example Utilizando SQL

Consultas: Exemplos

// Através de HQLsession.createQuery("from Category c where c.name like 'Laptop%'");

// Utilizando-se Criteriasession.createCriteria(Category.class).add(

Restrictions.like("name", "Laptop%"));

// Através de SQLsession.createSQLQuery(

"select {c.*} from CATEGORY {c} where NAME like 'Laptop%'").addEntity("c", Category.class);

Consultas

Envolve alguns passos Criar a consulta com as restrições necessárias Adicionar parâmetros à consulta Executar a consulta e recuperar o resultado

A forma de execução da consulta e obtenção dos dados pode ser configurada

Criando a Consulta

Query query = session.createQuery("from User");

Criteria criteria = session.createCriteria(User.class);

Objetos Query e Criteria são obtidos através do Session org.hibernate.Query org.hibernate.Criteria

Adicionando Parâmetros à Consulta

Parâmetros não devem ser adicionados na própria String da consulta "from Item i where i.description like '" + search + "'"

Evita-se ataque do tipo SQL Injection foo' and callSomeStoredProcedure() and 'bar' = 'bar

Parâmetros podem ser adicionados através de sua posição ou de seu nome

Adicionando Parâmetros pelo Nome

String queryString = "from Item item where item.description like :search";

Query q = session.createQuery(queryString).setString("search",searchString);

String queryString = "from Item item"+ " where item.description like :search"+ " and item.date > :minDate";

Query q = session.createQuery(queryString).setString("search",searchString).setDate("minDate", mDate);

Nome do parâmetro é precedido de “:” O valores são adicionados através de métodos

sets

Adicionando Parâmetros pela Posição

String queryString = "from Item item"+ " where item.description like ?" + " and item.date > ?";

Query q = session.createQuery(queryString).setString(0, searchString).setDate(1, minDate);

A consulta contém “?” para indicar a existência de alguma parâmetro

Os valores também são adicionado através de métodos sets

Executando a Consulta

Se mais de um objeto pode ser retornado, chama-se o método list() List list = query.list();

Se somente um objeto pode ser retornado, chama-se o método uniqueResult() User user = (User) query.uniqueResult(); O método retorna null se nenhum objeto for

encontrado Se a consulta retornar mais de um objetos, a exceção

NonUniqueResultException é lançada

Executando a Consulta

Query query = session.createQuery("from User");List<User> list = query.list();

for (User user : list) {System.out.println(user.getName());

}

Query query2 = session.createQuery("from User user where user.name =:name").setString("name","SUNSP");

User user = (User) query2.uniqueResult();

System.out.println(user.getName());

Consultas Básicas A consulta mais simples tem somente a cláusula

FROM “from Item”

Para se referenciar as propriedades de uma entidade, um ALIAS deve ser criado “from Item as item” “from Item item”

Palavra chave “as” é opcional A consulta não é case-sensitive

“FROM Item AS item” também pode ser utilizada

Consultas Polimórficas

Consultas podem ser escritas utilizando polimorfismo

“from BillingDetails” Retorna todas as entidades que herdam de

BillingDetails CreditCard BankAccount

A superclasse não precisa estar mapeada “from java.lang.Object” “from java.io.Serializable”

Restrições

Geralmente não se quer trazer todo o conteúdo da tabela

Restrições devem ser adicionadas para restringir os objetos retornados

HQL também utiliza-se a cláusula WHERE As restrições são feitas sobre propriedades da

entidade

Restrições

Literais e condições podem ser incluídos Utiliza-se aspas simples para literais do tipo String “from User u where u.email = 'foo@hibernate.org'” “from Item i where i.isActive = true”

Comparações podem ser realizadas “from Bid bid where bid.amount between 1 and 10” “from Bid bid where bid.amount > 100” “from User u where u.email in ('foo@bar', 'bar@foo')”

Operador LIKE pode ser utilizado “%” representa qualquer seqüência de caracteres _ (Under_Score) representa qualquer caractere “from User u where u.firstname like 'G%'” Negação pode ser utilizada

“from User u where u.firstname not like '%Foo B%'” Operadores lógicos e parênteses

“from User user where user.firstname like 'G%' and user.lastname like 'K%'”

Comparações

Comparações

Operadores lógicos e parênteses “from User u where (u.firstname like 'G%' and

u.lastname like 'K%' ) or u.email in ('foo@hibernate.org', 'bar@hibernate.org' )”

Coleções "from Item i where i.bids is not empty"

Comparações

Funções podem ser chamadas a partir do HQL HQL permite a chamada de funções SQL na cláusula

WHERE Funções podem ser definidas pelo usuário

Depende do suporte do banco de dados Funções UPPER() e LOWER()

"from User u where lower(u.email) = 'foo@hibernate.org'" Função SIZE()

from Item i where size(i.bids) > 3 E muitas outras...

Comparações

Outras funções CONCAT(s1, s2) SUBSTRING(s, offset, length)

Offset começa a partir de 1 TRIM( [[BOTH|LEADING|TRAILING] s)

"from Item i where TRIM(BOTH i.name) = 'Computador'" LENGTH(s) LOCATE(search, s, offset)

Procura a localização de uma substring dentro de uma string

Comparações

Outras funções CURRENT_DATE(), CURRENT_TIME(),

CURRENT_TIMESTAMP() Valores retornados são referentes ao SGBD

SECOND(d), MINUTE(d), HOUR(d), DAY(d), MONTH(d), YEAR(d)

Extraem os valores de um argumento temporal

Ordenando o Resultado

A Cláusula ORDER BY é utilizada para ordenar o resultado "from User u order by u.name"

Ordem ascendente ou descendente Utiliza-se asc ou desc from User u order by u.username desc

Ordenando por mais de uma propriedade “from User u order by u.lastname asc, u.firstname asc”

Junções

A habilidade de realizar junções é uma das principais forças do modelo relacional

Permite selecionar diferentes objetos associados e coleções em uma única consulta

Inner Join

Contém somente os registros que estão relacionados com o outro lado da junção Contém somente os Itens que possuem Bids

(left) Outer Join

Retorna todos os Itens Dados de Bid são preenchidos com NULL se não

houver uma correspondência

Junção com HQL

Coluna de junção não precisar ser informada na consulta Informação é extraída do mapeamento

É necessário ser informado somente o nome da associação Nome do atributo que referencia a classe ou coleção

de classes Joins podem ser executados de duas maneiras

Join implícitos na Associação Join especificado na cláusula FROM

Join Implícito na Associação

O Join é realizado através da associação entre duas entidades

Exemplo: “from Bid bid where bid.item.description like '%Foo%'” Bid é associado a Item através do atributo “item” Hibernate sabe que a associação está mapeada a partir da

chave estrangeira ITEM_ID da tabela BID Joins implícitos são sempre realizados através de

associações many-to-one ou one-to-one

Join Implícito na Associação

Múltiplos joins são possíveis from Bid bid where bid.item.category.name like 'Laptop%'

Join especificado na Cláusula FROM

Joins podem ser especificados explicitamente na cláusula FROM

Exemplo "select i from Item i join i.bids b where b.amount >

10" Aliases devem ser especificados na cláusula

FROM e utilizados na cláusula WHERE Cláusula SELECT é utilizada para que somente

Itens sejam retornados

Join especificado na Cláusula FROM

Na consulta, um Item pode ser retornado mais de uma vez Uma para cada Bid associado Somente uma instância é utilizada

A consulta possui o mesmo formato para associações many-to-one e one-to-one

Outer Joins

Para a utilização de Outer Joins utiliza-se a cláusula LEFT JOIN LEFT OUTER JOIN e RIGHT OUTER JOIN

também podem ser utilizados A cláusula WITH é utilizada para adicionar

restrições "select i from Item i left join i.bids b with

b.amount >= 9" Itens que não possuem Bids também são retornados

Comparando Identificadores

Na Orientação a Objetos não são os identificadores, mas as referências ao objetos que são comparadas

Comparações podem ser feitas através dos atributos das entidades

"select i from Item i join i.bids b where i.seller = b.bidder" i.seller e b.bidder representam referências para a

entidade User Retorna os Itens em que o próprio vendedor deu um

lance

Comparando Identificadores

Entidades também podem ser adicionadas como parâmetros de uma consulta

Query query = session.createQuery("from Item i where i.seller = :seller");

query.setEntity("seller", user);

FIM...

Referências Livro Java Persistence with Hibernate

Edição revisada do livro Hibernate in Action Autores

Christian Bauer Gavin King

http://www.hibernate.org/ Documentação e Javadoc Fórum de discuções e dúvidas

JSR 220: Enterprise JavaBeans Version 3.0 Para as Annotations do pacote javax.persistence