Este documento fornece uma introdução à Java Persistence API (JPA), abordando tópicos como: 1) relacionamentos entre objetos; 2) mapeamento objeto-relacional; 3) introdução prática com um exemplo "Hello World" utilizando JPA.
5. Objetos que são coisas: entidades
• Interface para dados: estado
• Classes geralmente possuem várias instâncias,
claramente identificadas
• Business Objects
• Produtos, Itens, Pedidos, Clientes
• Métodos operam sobre seus próprios dados e
sobre objetos dependentes
6. Objetos que fazem coisas: serviços
• Interface para operações: comportamento
• Classes geralmente representam uma única
instância (singleton)
• Dados (estado) compartilhados
• Application Services
• Fachadas, DAOs, Serviços, Camadas
• Métodos utilizam entidades e outros serviços
para realizar casos de uso
7. Tipos de relacionamento
• Ligação fraca: uso / associação
– Navio / Porto: porto existe sem navio e navio existe
sem porto e a destruição de um não afeta o outro
– Porto e Navio devem ter interfaces compatíveis (para
permitir que Navio possa atracar)
• Ligação forte: agregação
– Navio / Carga: cada container de carga acrescenta
(agrega) algo ao navio
– Cascade-delete: se navio afundar, a carga vai junto
• Ligação muito forte: composição
– Navio / Casco: o navio não pode ser utilizado
(inicializado) sem que seu casco esteja pronto antes
8. Em Java: associação
• Associação bidirecional (porto conhece seus navios e cada
navio sabe em que porto está)
class Porto {
private Set navios = new Set();
public Porto() {}
public void addNavio(Navio n) {
navios.add(n);
n.atracar(this);
}
public Set getNavios() {
return navios;
}
}
class Navio {
Porto porto;
public Navio() {}
public void atracar(Porto p) {
porto = p;
}
public boolean atracado() {
return porto != null;
}
}
9. Em Java: agregação
class Carga {
private double peso;
public Carga(double p) {
peso = p;
}
public double getPeso() {
return peso;
}
public double destruir() {
peso = 0.0;
}
}
class Navio {
private Set<Carga> containers;
private double peso = 100.0;
public Navio() {}
public void carregar(Carga c) {
containers.add(c);
}
public void afundar() {
for(Carga c: carga)
carga.destruir();
}
public double pesar() {
for(Carga c: carga) {
peso += carga.getPeso();
}
}
Agregação unidirecional (navio
conhece suas cargas, mas Carga
desconhece em que navio está)
10. Em Java: composição
class Estaleiro {
private Navio montar() {
Casco c = new CascoLongo();
Leme m = new LemeHidraulico();
return new Navio(c, m);
}
}
class Navio {
private Casco casco;
private Leme leme;
public Navio(Casco c, Leme m) {
casco = c;
leme = m;
}
public void virarDireita() {
leme.virarEsquerda();
}
public void virarEsquerda() {
leme.virarDireita();
}
}
class CascoLongo implements Casco {
int comprimento = 100;
int largura = 10;
}
abstract class Leme {
abstract void virarEsquerda();
abstract void virarDireita();
}
. . .
11. Cardinalidade
• Um para muitos, bidirecional
• Um para um, bidirecional
• Muitos para muitos, bidirecional
class Produto {
Item item;
}
class Item {
Produto produto;
}
class Pedido {
Set<Item> itens;
}
class Item {
Pedido pedido;
}
class Aluno {
Set<Turma> turmas;
}
class Turma {
Set<Aluno> alunos;
}
Os atributos
representam estados
visíveis (métodos get/
set)
12. Direção e visibilidade
• Um para muitos, unidirecional
• Muitos para um, unidirecional
• Um para um, unidirecional
class Produto {
}
class Item {
Produto produto;
}
class Pedido {
Set<Item> itens;
}
class Item {
}
class Pedido {
}
class Item {
Pedido pedido;
}
equivalentes
13. 2. Introdução ao mapeamento objeto
relacional (ORM)
• O que é ORM e como funciona?
• Dificuldades da ORM: incompatibilidades
• Usando uma camada de persistência JDBC
• Exemplo: tabelas e objetos + DAO
14. O que é ORM
• Object Relational Mapping
• Mapeamento de classes a esquemas de
bancos de dados relacionais
– Em tempo de configuração
• Utilização de uma API de persistência
– Em tempo de desenvolvimento
• Persistência transparente de objetos como
registros de um banco de dados
– Em tempo de execução
15. Por que usar ORM?
• Facilitar o desenvolvimento
– Simplificar, facilitar manutenção
– Separar responsabilidades
• Unir os benefícios de dois mundos
– Preservar as vantagens do paradigma relacional
(robustez, maturidade, facilidade de pesquisa, etc.)
para a camada de persistência
– Preservar as vantagens do paradigma orientado a
objetos (reuso, modularidade, herança,
polimorfismo, etc.) para a camada de negócios
16. Como funciona?
• Tempo de configuração
– Classes mapeadas a tabelas (esquemas)
• Tempo de execução
– Instâncias (objetos) automaticamente mapeadas a registros
conta correntista saldo
1 Gargantua 1370
2 Pantagruel 3450
3 Gargamel 800
4 Morticia 8200
Classe Conta
String codigo
String nome
double saldo
instância:Conta
codigo="4"
nome="Morticia"
saldo=8200
Tabela Conta
Banco de Dados Relacional
17. Mas...
• Nem sempre o modelo relacional é equivalente
ao modelo de objetos
– O modelo normalizado mais eficiente geralmente tem
menos tabelas que os objetos do modelo de objetos
mais bem projetado
– Há objetos dependentes que são parte de um objeto
maior (colunas de uma tabela)
– Há objetos que são apenas visões de dados (dados de
várias tabelas)
• Uma boa ferramenta de ORM permite configurar
essas incompatibilidades
18. Mapeamento ideal
• Neste exemplo, o descasamento entre o
paradigma objeto e relacional não aparece
public class Cliente {
private String userid;
private String nome;
private String endereco;
private Set contas;
// (get/set), etc. ...
}
public class Conta {
private String numero;
private String nome;
private String tipo;
private Cliente usuario;
// métodos, get/set...
}
create table CLIENTE (
USERID VARCHAR(15) NOT NULL PRIMARY KEY,
NOME VARCHAR(50) NOT NULL,
ENDERECO VARCHAR(100)
)
create table CONTA (
NUMERO VARCHAR(10) NOT NULL PRIMARY Key,
NOME VARCHAR(50) NOT NULL,
TIPO VARCHAR(2) NOT NULL,
USUARIO VARCHAR(15) FOREIGN KEY REFERENCES CLIENTE
)
Foreign key USUARIO realiza o
relacionamento
19. Problema: granularidade
• Vários níveis são possíveis no modelo de objetos
– Baixa: Cliente com endereco (String)
– Mais alta: Endereco é objeto com propriedades: como cep,
cidade, rua, país, etc.
– Ainda mais alta: Rua é objeto com propriedades numero, etc.
• Há dois níveis apenas no modelo relacional
– Tabela (CLIENTE) e coluna (USERID, ENDERECO, etc.)
• Solução: múltiplos objetos mapeados a uma tabela
create table CLIENTE (
USERID VARCHAR(15) NOT NULL PRIMARY KEY,
NOME VARCHAR(50) NOT NULL,
ENDERECO_RUA VARCHAR(50),
ENDERECO_CIDADE VARCHAR(15),
ENDERECO_ESTADO VARCHAR(15),
ENDERECO_CEP VARCHAR(8),
ENDERECO_PAIS VARCHAR(15)
)
Cliente
Endereco
20. Problema: tipos e subtipos
• Modelo relacional não suporta herança nem
polimorfismo
– Queremos escrever queries que referem-se à classe
Conta e retornar instâncias concretas dessa classe!
• Requer implementação bem mais complexa
Associação
polimórfica!
Cliente Conta
ContaCredito ContaDebito
*
21. Problema: identidade
• No mundo relacional, existe um critério de igualdade:
– Chave-primária
• No mundo Java há dois
– Igualdade de referência (testado com ==)
– Equivalência (testado com equals())
• Além disso, mapeamento pode associar vários objetos a
uma mesma tabela!
• Complicações adicionais e problemas
– Chaves naturais
– Chaves compostas
– Ausência de chave
– Chaves que podem ser modificadas
problema de design:
deve ser corrigido
devem ser evitadas
em sistemas novos
22. Problema: associações
• Java representa associações como referências
(ou coleções de) referências para objetos
– São inerentemente direcionais
– Para implementar associações bidirecionais, é preciso
criar referências dos dois lados da associação
– Referências duplas podem ser associações M-N
• No mundo relacional, associações são
representadas por chaves estrangeiras
– Não são inerentemente direcionais
– Pode-se criar associações com projeção e joins
– Associações M-N requerem tabela extra
23. Problema: navegação em grafos
• Navegação em objetos
– Pula-se de um objeto para outro: obj.getA().getB()
sem que haja um caminho previamente definido
– O equivalente em SQL seria fazer um query para cada
pulo (nó do grafo)
– Portanto, a forma mais natural de navegar entre
objetos em OO é a forma menos eficiente de
recuperar dados em SQL
• Joins podem minimizar queries
– Mas perde-se a flexibilidade da definição dinâmica do
caminho de navegação
24. Solução
• Usar uma camada de persistência
para lidar com incompatibilidades
entre os paradigmas
– Usar uma arquitetura com separação
em camadas
– Cada camada concentra-se em um
interesse predominante
– Uma das camadas cuida da persistência
• Como implementar
– Escrever uma camada de persistência
(ex: DAO JDBC)
– Usar uma solução como ORM
(ex: Hibernate)
Camada de Apresentação
Camada de Negócios
Camada de Persistência
25. 3. Introdução prática: Hello World
• Passo-a-passo na construção de uma
aplicação JPA simples
– Criação de um projeto
– Criação dos objetos e das tabelas
– Definição dos mapeamentos
– Configuração do ambiente
– Implementação da persistência
• Siga os passos na sua máquina
26. Criação do projeto
• Crie um novo projeto no NetBeans
– Pacote raiz: jpaloja
• Dependências
– Inclua todos os JARs do diretório lib/ da distribuição
do Hibernate
– Inclua o driver JDBC do banco de dados
• Crie uma base de dados “aula” no MySQL
– Anote também o nome de usuário e senha para
acesso ao banco de dados
27. Próximos passos
• Criar tabela no banco
– Opcional: nós vamos gerar a tabela a partir do
próprio objeto Java anotado
• Criar o objeto (POJO)
• Criar as anotações de mapeamento entre o
POJO e a tabela (três anotações)
• Configuração
– Criar arquivo de configuração da persistência
(persistence.xml)
• Utilização
– Implementar classe de testes e usar a API
28. Regras para construção de POJOs
• Regras para construção de JavaBeans
– Construtor sem argumentos com visibilidade mínima de pacote
– Expor estado usando métodos get/set
• Exemplo: para propriedade do tipo String “texto” use
– String getTexto() e
– void setTexto(String texto)
• Métodos get/set devem seguir as convenções JavaBean
mas não precisam ser públicos
– O JPA enxerga os métodos private
• Métodos get/set a outras entidades determinam
associações e permitem navegação no grafo de objetos
29. Identidade
• O objeto que será mapeado via JPA deve
ter um campo que possa ser usado como
identificador
– Independente do objeto já ter algum atributo
unívoco (como userid)
• Deve ser um campo inteiro numérico (int,
Integer, long, Long)
– Será usado pelo JPA para controlar a
persistência
30. POJO: classe Produto
package jpaloja.pojos;
public class Produto {
private Long codigo;
private String nome;
private double preco;
public Produto() { } // construtor vazio (necessário)
public Produto(String nome, double preco) {
this.nome = nome;
this.preco = preco;
}
public Long getCodigo() {
return codigo;
}
private void setCodigo(Long codigo) {
this.codigo = codigo;
}
// outros métodos get/set (...)
}
Crie o POJO no pacote
jpaloja.pojos
O Netbeans guardará na pasta src
do projeto
O Netbeans gera
automaticamente o construtor
utilitário (com argumentos) e os
métodos get/set
31. Tabela
• Iremos mapear o Produto à seguinte tabela
• Não precisa criar a tabela neste momento pois
iremos gerá-la
CREATE TABLE produto
(
codigo int8 NOT NULL,
preco float8,
nome varchar(255),
CONSTRAINT produto_pkey PRIMARY KEY (codigo)
)
32. Mapeamento
• Acrescente as seguintes anotações de mapeamento no
arquivo Produto.java
package jpaloja.pojos;
import javax.persistence.*;
@Entity
public class Produto {
@Id
@GeneratedValue (strategy=GenerationType.AUTO)
private Long codigo;
...
}
33. Mapeamento: tabelas e classes
• O mapeamento da classe à tabela é realizada através da
anotação @Entity
– Se o nome da tabela não for informado, ela terá o mesmo nome
que a classe
package jpaloja.pojos;
import javax.persistence.*;
@Entity
@Table(name = “PRODUTO”)
public class Produto {
...
}
34. Mapeamento: identificador
• O identificador é mapeado através da anotação @Id à chave
primária da tabela
• A anotação @GeneratedValue informa como a chave será
incrementada pelo provedor de persistência
package jpaloja.pojos;
import javax.persistence.*;
@Entity
public class Produto {
@Id
@GeneratedValue (strategy=GenerationType.AUTO)
private Long codigo;
}
35. Mapeamento: propriedades
• As propriedades escalares são mapeadas através da anotação
@Column
– Se ela for omitida, todos os métodos automaticamente serão
considerados parte da interface de mapeamento
@Entity
@Table(name = “PRODUTO”)
public class Produto {
@Column(name = “CODIGO”)
private Long codigo;
@Column(name = “NOME”)
private String nome;
@Column(name=”PRECO”)
private double preco;
}
36. Configuração da persistência
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" ... >
<persistence-unit name="LojaVirtual">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<!-- <jta-data-source>java:/LojaVirtualDS</jta-data-source> -->
<class>lojavirtual.Produto</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
<property name="hibernate.connection.username" value="root"/>
<property name="hibernate.connection.password" value=""/>
<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/test"/>
<property name="hibernate.transaction.manager_lookup_class"
value="org.hibernate.transaction.JBossTransactionManagerLookup"/>
<property name="hibernate.show_sql" value="true"/>
<!-- <property name="hibernate.hbm2ddl.auto" value="create"/> -->
</properties>
</persistence-unit>
</persistence>
Descomente na primeira
execução para gerar as
tabelas, depois comente
de novo.
Guarde em src/META-INF com
o nome persistence.xml
37. Utilização da persistência
• Para usar o mecanismo de persistência do JPA, é
preciso inicializar uma sessão do EntityManager
– O EntityManager oferece uma API de operações de
persistência (gravação, leitura, transações)
– Cada sessão ocupa um thread e é obtida a partir de
um EntityManagerFactory ou via injeção de
dependências (em EJB 3)
• No nosso exemplo usaremos EJB 3
– Será preciso criar um EJB (Session Bean) para
gerenciar a persistência, e disponibilizá-lo no JBoss
38. Principais interfaces
• Todas do pacote javax.persistence
• Persistence
– Oferece um factory method para criar EMF
• EntityManagerFactory
– Equivalente a SessionFactory do Hibernate
• EntityManager
– Equivalente a Session do Hibernate
• Query
– Equivalente a Query do Hibernate
• EntityTransaction
– Equivalente a Transaction do Hibernate
39. Alguns métodos de EntityManager
• void persist(E objeto)
– Insere objeto na camada de persistência
• E find(E.class, Object id)
– Localiza objeto pelo ID
• E merge(E objeto)
– Atualiza objeto (e cascade, se houver)
• Query createQuery(String jpql)
– Cria um query
• Outros:
– remove(E objeto), flush(), close(), getTransaction()
40. Inserir
package loja;
import jpaloja.pojos.Produto;
import javax.persistence.*;
public class Inserir {
public static void main(String[] args) {
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("LojaVirtual");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Produto p1 = new Produto("Giroscopio", 123.89);
Produto p2 = new Produto("Tesoura", 23.14);
em.persist(p1);
em.persist(p2);
tx.commit();
em.close();
}
}
41. Listar
package loja;
public class Listar {
public static void main(String[] args) {
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("LojaVirtual");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Query q = em.createQuery("select p from Produto p”);
List produtos = q.getResultList();
System.out.println( produtos.size() + “ produtos encontrados!”);
for (Object o: produtos.getResultList()) {
Produto p = (Produto) o;
System.out.println( p.getNome() + “ $” + p.getPreco() );
}
tx.commit();
em.close();
}
}
42. SessionContext do EJB
• Para obter, use injeção de recursos
@Resource
SessionContext ctx;
• A partir do contexto, pode-se localizar objetos e recursos via
JNDI
DataSource ds = ctx.lookup("java:/DefaultDS");
EntityManagerFactory emf = ctx.lookup("emf/loja");
Queue fila = ctx.lookup("jms/Queue");
• É melhor obter um EntityManager usando injeção de recursos
com @PersistenceContext
@PersistenceContext
private EntityManager em;
43. EJB usando persistência
@Stateless
public class ProdutoFacadeBean implements ProdutoFacadeBeanRemote {
@PersistenceContext(unitName="LojaVirtual")
private EntityManager em;
public List<Produto> retrieveAll() {
Query query = em.createQuery("select p from Produto p");
List<Produto> produtos = query.getResultList();
return produtos;
}
public Produto insert(Produto p) {
em.persist(p);
return em.merge(p);
}
...
44. Execução
• Isto é um cliente EJB remoto
– Sua execução requer a configuração de um cliente Java EE (contendo JARs /
client do JBoss + jndi.properties informando a localização do servidor)
public class ProdutoTest {
public static void main(String[] args) throws NamingException {
ProdutoFacadeBeanRemote facade;
Context jndi = new InitialContext();
Object obj = jndi.lookup("ProdutoFacadeBean/remote");
facade = (ProdutoFacadeBeanRemote) obj;
Produto p1 = new Produto();
p1.setNome("Produto 1");
p1.setPreco(30.00);
Produto p2 = new Produto();
p2.setNome("Produto 2");
p2.setPreco(50.00);
facade.insert(p1);
facade.insert(p2);
...
45. JP-QL
• JP Query Language é um dialeto orientado a
objetos do SQL
– Não é uma linguagem de manipulação de dados
(como SQL)
– Não serve para inserir, remover, atualizar (tem como
fazer, mas não se recomenda)
– É usada apenas para recuperação de objetos
• Exemplo
Query q =
session.createQuery("select u from User u where u.firstname = :fname");
q.setString("fname", "Max");
List result = q.getResultList();
46. 4. Mapeamento de entidades
• A classe deve ser descrita como entidade
– anotada com @Entity (ou declarada como <entity> no
deployment descriptor orm.xml)
– ter um campo @Id (a menos que participe como subclasse de
uma hierarquia mapeada em JPA)
• Deve ter construtor sem argumentos
• Não pode
– ser interface ou um enum ou classe final
– ter variáveis de instância ou métodos finais
• Deve implementar java.io.Serializable se precisar ser
passada por valor
– Ex: se objeto for usado por um cliente remoto, como em EJBs
que têm interface @Remote
47. Entidades
• Suportam herança, associações polimórficas,
queries polimórficos
– Podem estender classes comuns e vice-versa
– Somente entidades podem ser usadas em queries JPA
• Seu estado persistente é representado pelas
suas variáveis de instância
– acessado via propriedades (métodos get/set) ou
através de suas variáveis de instância
– variáveis de instância não declaradas transient e que
não tenham a anotação @Transient são consideradas
persistentes.
48. Entidades
• Assinaturas válidas para propriedades:
– Propriedades escalares (value-types) ou lado unitário de
associações com entidades (@Column):
T getProperty() e void setProperty(T t)
– Coleções de escalares (value-types) ou lado múltiplo de
associações com entidades (@JoinColumn)
Set<T> getProperty() e void setProperty(Set<T>)
(ou Collection, List ou Map)
• Tipos permitidos:
– Value-types: primitivos, String, BigInteger, BigDecimal, Date,
Calendar, Time, Timestamp, byte[], Byte[], char, Character[],
enums e coleções desses valores
– Tipos serializáveis e coleções de tipos serializáveis
– Entidades e coleções de entidades
49. Exemplo de mapeamento
@Entity
public class Cliente implements Serializable {
@Id
private Long id;
private String nome;
private Endereco endereco;
@OneToMany
private Set<Pedido> pedidos = new HashSet();
@ManyToMany
private Set<Pagamento> formasPagamento = new HashSet();
public Cliente() {}
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
...
public Collection<Order> getPedidos() { return pedidos; }
public void setPedidos(Collection<Pedido> pedidos) { this.pedidos = pedidos; }
public Set<Pagamento> getFormasPagamento() { return formasPagamento; }
...
}
Usando o mínimo de anotações!
Anotações ausentes (ex:
@Column, @Table, etc.) resultam
em comportamento default (ex:
tabela com mesmo nome que
classe)
As anotações também possuem
muitas propriedades que podem
ser configuradas; se omitidas, são
usadas as propriedades default
50. Ciclo de vida de uma entidade:
breve resumo
• A operação new cria uma instância no estado transiente
– Entidade transiente = new Entidade()
• Para tornar a instância persistente é preciso utilizar a
API do EntityManager
– entityManager.persist(transiente);
– Entidade persistente = entityManager.merge(transiente);
• Toda entidade tem uma chave primária.
– Indicada pela anotação @Id (pode ser na superclasse) e
geralmente definida pelo serviço de persistência
– Aplicação nunca deve mudar valor de chave primária.
• Fora do escopo do EntityManager, objeto que já foi
persistente é considerado desconectado (detached)
– Pode ser re-ligado com um merge:
Entidade persistente = entityManager.merge(detached);
51. 5. Mapeamento de associações
• Associações no JPA funcionam da mesma maneira que
associações de objetos em Java
– Associações de objetos em JPA são naturalmente unidirecionais
e não são gerenciadas pelo container (como em EJB 2.1)
• Associações são sempre relacionamentos entre
entidades (diretamente ou via coleções)
• Três tipos
– @OneToOne
– @ManyToOne
– @ManyToMany
• Duas possibilidades para cada tipo
– Unidirecional
– Bidirecional
52. Associações
• @ManyToOne é a associação mais comum
– O modelo relacional é naturalmente um-para-muitos
– Uma referência simples de objeto é um-para-muitos
– Em associações bidirecionais, do outro lado deve haver @OneToMany
• @OneToOne requer constraints (que são gerados) nas tabelas para
garantir consistência da associação
• @ManyToMany mapeia três tabelas a dois objetos
• Em associações bidirecionais, há anotações de ambos os lados
– Atributo mappedBy é usado em associações bidirecionais para
informar o nome do atributo da outra classe que faz a associação
– @OneToOne CPF cpf;
à @OneToOne(mappedBy="cpf") Cliente cliente;
– @ManyToMany(mappedBy="alunos") Set<Turma> turmas;
à @ManyToMany(mappedBy="turmas") Set<Aluno> alunos;
– @ManyToOne(mappedBy="item") Set<Produto> produtos;
à @OneToMany Item item;
53. @ManyToOne
• Pode-se mapear tudo com @ManyToOne
• Associações @OneToOne são raras
– Muitas vezes representam composições que devem ser
preferivelmente implementadas usando @Embedded /
@Embeddable (veremos na parte 3, adiante)
• Associações @ManyToMany podem ser implementadas
com três objetos e duas associações @ManyToOne
– As vezes isto é mais simples e adequado, quando se precisa de
um objeto mapeando a associação (join-table)
• Várias anotações e atributos permitem configurar
ajustes finos dos mapeamentos
• Escolha a solução mais simples e adequada ao seu
projeto
56. @ManyToMany
• Default: @ManyToMany nos Set de cada lado da
associação, informando o campo de mapeamento
(mappedBy)
– Em associações bidirecionais, é preciso atualizar os dois lados
– Usando-se Set e mappedBy, garante-se a não duplicação de
dados quando objeto é adicionado duas vezes na coleção
• Muitos ajustes possíveis
– Uso de List ou Collection (com ou sem @CollectionID) ou Map
(com @MapKey)
– Configuração de @JoinTable, @JoinColumn, @SecondaryTable
• Essas configurações avançadas não serão abordadas
neste curso
58. @OneToOne
• Muitas vezes é desnecessário -relacionamento pode ser
representado de outras formas
– Pode ser @ManyToOne unidirecional
– Pode ser implementado com o componente na mesma tabela
(@Embedded)
• Duas formas de mapeamento @OneToOne
– Associação de chave estrangeira (default)
– Associação de chave primária (não pode definir um
@JoinColumn, mas é anotado com @PrimaryKeyJoinColumn
• Neste curso utilizaremos apenas o mapeamento default
60. Cascades: persistência transitiva
• Para acrescentar um item em um pedido, é preciso atualizar dois
objetos
Pedido p = new Pedido();
Item i = new Item();
p.addItem(i); // atualiza dois objetos
• Para persistir essa alteração, também é preciso sincronizar os dois
objetos
em.persist(p);
em.persist(i);
• O ideal é fazer isto automaticamente: cascade
@OneToMany(cascade={CascadeType.PERSIST},
mappedBy="item" )
private Set<Item> itens = new HashSet<Item>();
– Quando um Pedido for persistido no banco, quaisquer itens que
estiverem na sua hierarquia de objetos também serão persistidos
61. CascadeType
• O atributo cascade recebe um array {} de opções
• Há várias opções para CascadeType. Algumas das mais
importantes são
– PERSIST
– MERGE
– REMOVE
• Normalmente, usa-se PERSIST com MERGE (inserts e
updates feitos automaticamente)
@OneToMany(cascade={CascadeType.PERSIST, CascadeType.MERGE})
• Pode-se usar CascadeType.ALL para que todas as
operações sejam transitivas
62. Recuperação de dados: lazy
• Se você usar um cliente externo que não mantém aberta
a sessão do EntityManager, poderá ter exceções de
inicialização lazy (inicialização preguiçosa) quando for
acessar objetos dentro de coleções
– O query, por default, só retorna a coleção com os ponteiros
para os objetos, e eles só podem ser recuperados do banco se
houver uma sessão aberta
– Como lidar com isto será tratado mais adiante
• Uma forma rápida de resolver o problema (por
enquanto) é declarar nas associações o atributo
fetch=FetchType da forma
@OneToMany(mappedBy="pedido", fetch=FetchType.EAGER)
private Set<Item> itens = new HashSet<Item>();
63. Outras anotações de associações
• @JoinColumn(name="coluna", ...)
– Propriedades que não são associações informam a
coluna da tabela a qual estão mapeadas com
@Column
– Associações informam a coluna da associação (foreign
key) usando @JoinColumn
– Se não informada, o sistema assume que a coluna
tem o mesmo nome que o atributo
• Consulte a especificação para maiores detalhes
sobre as outras anotações e seus atributos
64. 6. Mapeamento de composição
• Em ORM, objetos que formam uma composição
compartilham a mesma tabela
– Em JPA, classes que são componentes são mapeadas como
@Embeddable (e não como @Entity) e suas propriedades
anotadas como @Embedded na entidade que as contém
Cliente
nome
email
cartao
Endereco
Rua
CEP
Cidade
enderecoEntrega
enderecoCobranca
nome email cartao ent_rua ent_cep ent_cidade cob_rua cob_cep cob_cidade
:Ciente enderecoEntrega:Endereco enderecoCobranca:Endereco
<<Entidade>> <<Componente>>
@Entity @Embeddable
@Embedded
65. Composições (@Embedded)
• O objeto embutido (componente) na entidade é a parte
dependente da relação de composição
– Uma composição ocorre quando uma entidade, que tem
identidade na tabela, está composta por objetos que não têm
identidade na tabela (seus dados são parte da tabela)
– A remoção do registro no banco remove a entidade e seus
objetos componentes (não confunda com cascade-delete)
• O objeto embutido é propriedade da entidade
– Só existem como objetos, mas não como tabelas
• Muitas associações 1-1 podem ser implementados como
objetos embutidos
– Ex: a associação Cliente-CPF mostrada anteriormente
66. Exemplo com @Embedded
@Entity
@Table(name="CLIENTE")
public class Cliente {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long codigo;
private String nome;
...
@Embedded
private Endereco enderecoCobranca;
@Embedded
@AttributeOverrides({
@AttributeOverride(name="rua", column=@Column(name="ENT_RUA")),
@AttributeOverride(name="cep", column=@Column(name="ENT_CEP")),
@AttributeOverride(name="cidade", column=@Column(name="ENT_CIDADE")),
})
private Endereco enderecoEntrega;
...
@Embeddable
public class Endereco {
@Column(name="COB_RUA")
private String rua;
@Column(name="COB_CEP")
private String cep;
@Column(name="COB_CIDADE")
private String cidade;
...
}
reusa mesma classe Endereco, mas
redefine mapeamentos
67. • Herança é o descasamento mais visível entre os mundos
relacional e orientado a objetos
– Mundo OO possui relacionamento “é um” e “tem um”
– Mundo relacional apenas possui relacionamento “tem um”
• Há várias estratégias [Ambler 2002]*
– Uma tabela por classe concreta: modelo relacional ignora
herança e polimorfismo (polimorfismo implícito)
– Uma tabela por hierarquia de classes: permite polimorfismo
com tabelas não normalizadas mais uma coluna extra contendo
informação de tipo
– Uma tabela por subclasse: representa relacionamentos “é um”
através de relacionamentos “tem um” (chave estrangeira)
7. Mapeamento de herança
68. a. Tabela por classe concreta
• Mapeia classes concretas a tabelas
@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class Pagamento {
@Id @GeneratedValue private long id;
}
Pagamento
id
valor
Credito
numero
validade
ID VALOR NUMERO VALIDADE
5
<<Entidade>>
@Entity
Debito
banco
conta
<<Entidade>>
@Entity
ID VALOR BANCO CONTA
5
CREDITO
DEBITO
@Entity
public class Debito extends Pagamento {...}
@Id declarado apenas na
superclasse
69. Tabela por classe concreta
• Vantagens
– Mapeamento direto entre classe e tabela normalizada
• Desvantagens
– Queries mais complexos e ineficientes
• O query polimórfico
select p from Pagamento p where p.valor > 1000
gera um SQL (conceitual) com subquery que concatena
queries nas tabelas com union:
– select ID, VALOR, NUMERO, VALIDADE, BANCO, CONTA from (
select ID, VALOR, NUMERO, VALIDADE,
null as BANCO, null as CONTA from CREDITO
union
select ID, VALOR, null as NUMERO, null as VALIDADE,
BANCO, CONTA from DEBITO
) where VALOR > 1000
70. b. Tabela por hierarquia de classes
@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="TIPO")
public abstract class Pagamento {
@Id @GeneratedValue private long id;
}
Pagamento
id
valor
Credito
numero
validade
ID TIPO VALOR NUMERO VALIDADE BANCO CONTA
5 crd 100.0 23459403 12/15 null null
6 deb 100.0 null null 1234-5 993423-3
<<Entidade>>
@Entity
Debito
banco
conta
<<Entidade>>
@Entity
PAGAMENTO
@Entity
@DiscriminatorValue("deb")
public class Debito extends Pagamento {...}
Coluna (DiscriminatorColumn)
presente na tabela mas sem
mapeamento na classe
71. b. Tabela por hierarquia de classes
• Mapeia-se a hierarquia inteira a uma única tabela
– Tabela inclui coluna (não mapeada) para identificar a classe
– Há colunas para todas as propriedades de todas as classes
• Vantagens
– Forma mais eficiente de implementar polimorfismo.
– Query polimórfico:
select ID, VALOR, BANCO, CONTA, NUMERO, VALIDADE from
PAGAMENTO where VALOR > 1000
– Query em classe concreta:
select ID, VALOR, BANCO, CONTA, NUMERO, VALIDADE from
PAGAMENTO where TIPO = 'deb' and VALOR > 1000
• Desvantagens
– Tabelas não normalizadas
– Colunas de propriedades declaradas em subclasses precisam
aceitar valores nulos (pode ser ruim para grandes hierarquias)
72. c. Tabela por subclasse
• Mapeia classes a tabelas
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
public abstract class Pagamento {
@Id @GeneratedValue private long id;
}
Pagamento
id
valor
Credito
id
numero
validade
ID NUMERO VALIDADE
9
<<Entidade>>
@Entity
Debito
id
banco
conta
<<Entidade>>
@Entity
ID BANCO CONTA
5
CREDITO
DEBITO
@Entity
public class Debito extends Pagamento {...}
ID VALOR
5
PAGAMENTO
ID é PK e FK
73. Uma tabela por subclasse
• Herança como relacionamentos de chave estrangeira
– Cada subclasse tem sua própria tabela
– Cada tabela possui colunas apenas para campos não-herdados, e
chave primária que é chave estrangeira da superclasse
– Recuperação de dados através de um join das tabelas
• Vantagens
– Modelo relacional normalizado
– Evolução e restrições de integridade simples
– Classes/tabelas criadas sem afetar classes/tabelas existentes
• Desvantagens
– Performance baixa em hierarquias complexas
– Difícil de codificar a mão (ruim para integrar com JDBC legado)
• Queries
– Outer join para pesquisas polimórficas, inner join para queries
em classes concretas
74. Qual estratégia usar?
• Se não houver necessidade de queries polimórficos ou
associações
– Tabela por Classe Concreta (TABLE_PER_CLASS)
• Se houver necessidade de associações polimórficas, e
hierarquia for simples
– Tabela por Hierarquia (SINGLE_TABLE)
• Se houver necessidade de associações polimórficas
mas hierarquia grande (ou não puder usar tabelas não
normalizadas)
– Tabela por Subclasse (JOINED)
75. 8. Queries JPQL
• Toda pesquisa no banco é feita através de Java
Persistence Query Language (JPQL)
– Linguagem de recuperação de dados similar a outras
linguagens de query de objetos (HQL, EJB-QL, etc.)
– Parece com SQL, mas é diferente: opera sobre objetos e não
tabelas – é mais simples
• Queries são objetos da classe Query
– Podem ser criados através métodos de EntityManager
– Instruções do query podem ser passadas diretamente na
criação do query ou declarados em anotações @NamedQuery
– Queries podem ser parametrizados
• Após sua execução, os dados podem ser recuperados
através de métodos da classe Query.
76. API essencial para Queries JPQL
• EntityManager
– Query createQuery("query jpql");
– Query createNamedQuery("nome de query");
• Query
– List<Tipo> getResultList();
– Object getSingleResult();
– Query setParameter(String var, Object obj);
Query setParameter(int pos, Object obj);
– int executeUpdate();
79. Sintaxe elementar
SELECT p.nome
FROM Produto AS p
WHERE p.codigo = '123'
Declara variável p como
sendo um Produto
O que vai ser
selecionado
Parâmetro
SELECT OBJECT (p)
FROM Produto p
WHERE p.nome = :n
AS é
opcional
Parâmetro do
método
Objeto (bean) sendo
selecionado
80. Palavras-chave JPQL
• Usadas na cláusula SELECT
– DISTINCT, OBJECT, AVG, MAX, MIN, SUM, COUNT
• Usadas na cláusula FROM
– IN, AS
• Usadas na cláusula WHERE
– FROM, WHERE, UPDATE, DELETE, JOIN, OUTER, INNER, LEFT,
GROUP, BY, HAVING, FETCH, OBJECT, NULL, TRUE, FALSE,
NOT, AND, OR, BETWEEN, LIKE, IN, AS, UNKNOWN, EMPTY,
MEMBER, OF, IS, ORDER, BY, ASC, DESC, MOD, UPPER,
LOWER, TRIM, POSITION, CHARACTER_LENGTH,
CHAR_LENGTH, BIT_LENGTH, CURRENT_TIME,
CURRENT_DATE, CURRENT_TIMESTAMP, NEW, EXISTS, ALL,
ANY, SOME
81. Cláusula FROM
• A cláusula FROM é quem informa o objeto
que está sendo pesquisado, e declara
variáveis usados no resto do query
– Cada variável tem um identificador e um tipo
– O identificador é qualquer palavra não-reservada
– O tipo é a classe do objeto marcado como @Entity
– A palavra-chave AS conecta o tipo ao identificador,
mas é opcional
FROM Produto AS p
Tipo Identificador
82. Cláusula SELECT
• A cláusula SELECT informa o que se deseja obter com
a pesquisa. Pode retornar
– Objetos (interfaces locais ou remotas)
– Atributos dos objetos
• A palavra SELECT pode ser seguida de DISTINCT para
eliminar valores duplicados nos resultados
• SELECT utiliza uma variável declarada na cláusula
FROM (opcionalmente pode usar OBJECT(p))
• Exemplos
SELECT p FROM Produto p
SELECT DISTINCT p FROM Produto p
SELECT p.codigo FROM Produto p
SELECT DISTINCT p.codigo FROM Produto p
Retorna
objetos
Retorna
campos
83. Cláusula WHERE
• A cláusula WHERE é opcional e restringe os resultados
da pesquisa com base em uma ou mais expressões
condicionais concatenadas
• As expressões podem usar
– Literais (strings, booleanos ou números)
– Identificadores (declarados no FROM)
– Operadores
– Funções
– Parâmetros do método que utiliza a pesquisa (?1, ?
2, :nome, ...)
• Literais
– Strings são representados entre apóstrofes: 'nome'
– Números têm mesmas regras de literais Java long e double
– Booleanos são TRUE e FALSE (case-insensitive)
84. Operadores
• Expressões matemáticas
– +, -, *, /
• Expressões de comparação
– =, >, >=, <, <=, <>
• Operadores lógicos
– NOT, AND, OR
• Outros operadores
– BETWEEN, NOT BETWEEN
– IN, NOT IN
– LIKE, NOT LIKE
– NULL, NOT NULL
– IS EMPTY, IS NOT EMPTY
– MEMBER, NOT MEMBER
• Operadores do LIKE
– _ representa um único caractere
– % representa uma seqüência de zero ou mais caracteres
– caractere de escape (necessário para usar _ ou %) literalmente
85. Algumas funções
• Manipulação de strings (usados em WHERE)
– CONCAT, SUBSTRING, TRIM, LOWER, UPPER
– LENGTH, LOCATE
• Funções aritméticas (usados em WHERE)
– ABS, SQRT, MOD, SIZE
• Data e hora (usados em WHERE)
– CURRENT_DATE, CURRENT_TIME,
CURRENT_TIMESTAMP
• Funções de agregação (usados em SELECT)
– COUNT, MAX, MIN, AVG, SUM
86. Exemplos
• Encontre todos os produtos que são chips e cuja margem de
lucro é positiva
– SELECT p
FROM Produto p
WHERE (p.descricao = 'chip'
AND (p.preco - p.custo > 0)
• Encontre todos os produtos cujo preço é pelo menos 1000 e no
máximo 2000
– SELECT p
FROM Produto p
WHERE p.preco BETWEEN 1000 AND 2000
• Encontre todos os produtos cujo fabricante é Sun ou Intel
– SELECT p
FROM Produto p
WHERE p.fabricante IN ('Intel', 'Sun')
87. Exemplos de JPA-QL (2)
• Encontre todos os produtos com IDs que começam com 12 e
terminam em 3
– SELECT p
FROM Produto p
WHERE p.id LIKE '12%3'
• Encontre todos os produtos que têm descrições null
– SELECT p
FROM Produto p
WHERE p.descricao IS NULL
• Encontre todos os pedidos que não têm itens (coleção)
– SELECT pedido FROM Pedido pedido WHERE pedido.itens
IS EMPTY
• Encontre todos os itens ligados a pedidos em coleção
– SELECT item
FROM Pedido pedido, Item item
WHERE item IS MEMBER pedido.itens
88. 9. Queries dinâmicos (JPA 2.0)
• Novidade do Java EE 6
– API que permite a construção de queries usando
objetos
– Inspirado em Criteria do Hibernate e JDO
– Pode-se descobrir erros nos queries em tempo de
compilação
• Queries são mais longos e podem ser mais complexos, mas
podem ser alterados dinamicamente
• Ideal para formulários de pesquisa onde queries são
montados de acordo com preferências do usuário
90. 10. DAO para JPA em EJB 3
• JPA não precisa de DAO quando usado em EJB 3, que
cuida dos contextos transacionais
• Mas um DAO ainda é importante
– Camada de persistência não precisa ser JPA (pode
ser JDBC direto ou usar bancos não relacionais
como Redis)
– Permite isolar queries (dinâmicos do JPA 2 ou JPQL)
• Usando Generics pode-se criar uma interface abstrata
reusável
91. DAO abstrato
public abstract class AbstractFacade<T> {
private Class<T> entityClass;
protected abstract EntityManager getEntityManager();
public AbstractFacade(Class<T> entityClass) {
this.entityClass = entityClass;
}
public void create(T entity) {
getEntityManager().persist(entity);
}
public void edit(T entity) {
getEntityManager().merge(entity);
}
public void remove(T entity) {
getEntityManager().remove(getEntityManager().merge(entity));
}
public T find(Object id) {
return getEntityManager().find(entityClass, id);
}
}