Upload
joel-lobo
View
18.715
Download
20
Embed Size (px)
DESCRIPTION
Citation preview
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 = '[email protected]'” “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 ('[email protected]', '[email protected]' )”
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) = '[email protected]'" 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