2. JAVAEE7 Sobre este tutorial
Este é um tutorial sobre tecnologia JPA (de acordo com a especificação Java EE 7) criado
para cursos presenciais e práticos de programação por Helder da Rocha.
Este curso aborda os conhecimentos essenciais para usar JPA na prática usando Hibernate
ou EclipseLink como provedor (consulte a documentação oficial para detalhes).
Este material poderá ser usado de acordo com os termos da licença Creative Commons BY-
SA (Attribution-ShareAlike) descrita em http://creativecommons.org/licenses/by-sa/3.0/br/
legalcode.
O código dos exemplos está disponível em repositório público no GitHub e é software livre
sob os termos da licença Apache 2.0.
3. JAVAEE7 Conteúdo
1. Introdução
2. Configuração do ambiente
3. Persistência
4. Mapeamento de entidades
5. Mapeamento de relacionamentos
6. Mapeamento de herança
7. Queries JPQL
8. Queries Criteria
9.Tuning: transações, cache e locks
5. JAVAEE7 O que é JPA?
•Java Persistence API: especificação Java EE que define um mapeamento
entre uma estrutura relacional e um modelo de objetos em Java
•Pode ser também usado de forma standalone, em aplicações Java SE,
utilizando um provedor de persistência independente
•Java EE 7 adota a especificação JPA 2.1 (JSR 338).
•JPA permite a criação de entidades persistentes - objetos gerenciados
que retém seu estado além do tempo em que estão na memória
•Oferece uma camada de persistência (CRUD de objetos) e um
mecanismo de mapeamento objeto-relacional (ORM) declarativo.
6. JAVAEE7 Object-Relational Mapping (ORM)
•Tempo de configuração
• Classes mapeadas a tabelas (esquemas)
•Tempo de execução
• Instâncias (objetos) automaticamente sincronizadas com 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
7. JAVAEE7 Camada de persistência
•JPA oferece uma API para interagir com objetos persistentes
mapeados através de ORM
•AAPI é proporcionada pela interface EntityManager e contém
•Métodos para gravar objetos em meio persistente (persist), remover
(remove) e atualizar (update)
•Métodos para construir e processar consultas usando scripts (JPA
Query Language) ou objetos (Criteria)
•Métodos para configurar o comportamento do cache de objetos
(que cuida da sincronização do estado de objetos e transações)
8. JAVAEE7 Configuração de JPA
•Para configurar JPA em uma aplicação é necessário que essa aplicação
tenha acesso, através do seu Classpath, aos seguintes componentes:
•Módulo contendo a API do JPA (as classes, interfaces, métodos,
anotações do pacote javax.persistence)
•Um provedor de persistência JPA (ex: Hibernate, EclipseLink) que
implementa as interfaces da API
•Drivers necessários para configuração do acesso aos datasources usados
(e pools de conexão, se houver)
•Um arquivo persistence.xml para configurar a camada de persistência.
9. JAVAEE7 Unidade de persistência
•O contexto de persistência, usado para sincronizar as instâncias
persistentes com o banco de dados é chamado de Entity Manager
•Controla o ciclo de vida das instâncias
•Pode ser instanciado pela própria aplicação (em aplicações standalone),
obtido via JNDI ou injeção de dependências (padrão em Java EE)
•O conjunto de entity managers e as entidades que eles gerenciam
configura uma unidade de persistência (Persistence Unit).
•Declarada no arquivo persistence.xml
•Referenciada em classes que usam objetos persistentes (DAOs, fachadas,
beans, servlets)
10. JAVAEE7 persistence.xml mínimo
•Deve estar no Classpath acessível pelas classes Java usadas na aplicação em
/META-INF/persistence.xml, e possui a seguinte configuração mínima:
•Este exemplo é utilizável em servidor Java EE que tenha sido configurado
para oferecer um datasource default (java:comp/DefaultDataSource).
•Normalmente aplicações usam datasources selecionadas de forma explícita.
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="MyPU" transaction-type="JTA">
</persistence-unit>
</persistence>
11. JAVAEE7 DataSource com JTA
•Este persistence.xml típico de uma aplicação JPA simples rodando em
um servidor Java EE 7 declara o nome da unidade de persistência e:
•o modelo de transações utilizada (JTA – gerenciada pelo container),
•o nome JNDI de um datasource específico (previamente configurado)
•uma única entidade mapeada
•uma única propriedade de configuração específica do provedor usado
<persistence version="2.1" …>
<persistence-unit name="com.empresa.biblioteca.PU" transaction-type="JTA">
<jta-data-source>jdbc/Biblioteca</jta-data-source>
<class>br.empresa.biblioteca.entity.Livro</class>
<properties>
<property name="eclipselink.deploy-on-startup" value="true" />
</properties>
</persistence-unit>
</persistence>
Os detalhes de conexão com o banco de dados
precisam ser configurados no servidor de aplicações
12. JAVAEE7 EclipseLink e PostgreSQL
•Exemplo de persistence.xml usado para acessar mesmos dados via
camada de persistência configurada localmente (transações gerenciadas
pela aplicação), usando provedor EclipseLink e banco PostgreSQL
<persistence version="2.1" … >
<persistence-unit name="com.empresa.biblioteca.PU"
transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>br.empresa.biblioteca.entity.Livro</class>
<properties>
<property name="javax.persistence.jdbc.driver"
value="org.postgresql.Driver" />
<property name="javax.persistence.jdbc.url"
value="jdbc:postgresql://localhost:5432/postgres" />
<property name="javax.persistence.jdbc.user" value="postgres" />
<property name="javax.persistence.jdbc.password" value="admin" />
</properties>
</persistence-unit>
</persistence>
13. JAVAEE7 Hibernate e MySQL
•Este persistence.xml configura unidade de persistência com transações locais
e provedor Hibernate acessando localmente um banco de dados MySQL
<persistence … >
<persistence-unit name="LojaVirtual" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<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"/>
</properties>
</persistence-unit>
</persistence>
14. JAVAEE7 Entidades e mapeamento
•A arquitetura do JPA baseia-se no mapeamento de tabelas a classes,
colunas a atributos
•O objetivo do mapeamento é construir uma hierarquia de entidades
•Entidade (Entity) é um objeto persistente. Em JPA é um JavaBean
(POJO) mapeado a uma tabela
•Uma entidade JPA pode ser mapeada a uma tabela de duas formas:
•Através de elementos XML no arquivo orm.xml
•Através de anotações em classes, métodos, atributos, construtores
15. JAVAEE7 Mapeamento em orm.xml
•Os únicos tags obrigatórios são <entity> e <id>, e os atributos name.
Pode-se ter mais detalhamento (ex: tipos, constraints) ou menos (ex:
omitir tags com nomes de tabela e colunas, se forem iguais).
<entity-mappings xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm
http://xmlns.jcp.org/xml/ns/persistence/orm/orm_2_1.xsd" version="2.1">
<entity name="Livro" class="br.com.argonavis.Livro" access="FIELD">
<table name="LIVRO"/>
<attributes>
<id name="id">
<column name="ID"/>
<generated-value strategy="TABLE" generator="LIVRO_SEQ"/>
</id>
<basic name="titulo"> <column name="TITULO"/> </basic>
<basic name="paginas"> <column name="PAGINAS"/> </basic>
</attributes>
</entity>
</entity-mappings> Se existir, deve estar localizado em META-INF/orm.xml
16. JAVAEE7 Mapeamento com anotações
•Para declarar uma entidade com anotações JPA é necessário no mínimo
anotar a classe com @Entity e anotar pelo menos um atributo (ou método)
com @Id, indicando sua chave primária
•Atributos expostos via getters/setters são mapeados por default a colunas
de mesmo nome
@Entity public class Livro implements Serializable {
@Id private Long id;
private String titulo;
private int paginas;
public Long getId() { return id;}
public void setId(Long id) { this.id = id;}
public String getTitulo() { return this.titulo;}
public void setTitulo(String titulo) { this.titulo = titulo;}
public int getPaginas() { return this.paginas;}
public void setPaginas(int paginas) { this.paginas = paginas;}
}
É preciso também determinar como o Id será
atribuído (se gerado pelo banco ou fornecido)
17. JAVAEE7 Mapeamento com anotações
•Pode-se mudar os defaults e realizar configurações adicionais é
usando anotações como @Table, @Column, @CollectionTable,
@JoinTable, @JoinColumn, etc.
@Entity @Table(name="BOOK")
public class Livro implements Serializable {
@Id
private Long id;
@Column(name="TITLE")
private String titulo;
@Column(name="PAGES")
private int paginas;
…
}
18. JAVAEE7 Exercícios: configuração
•Configuração do ambiente para realizar os exercícios
•Pode ser necessário adaptar os exercícios ao ambiente usado (banco de
dados, provedor de persistência, IDE, ambiente Java EE ou standalone,
duração do treinamento)
•Alguns exercícios usam arquivos disponíveis no repositório GitHub
•Scripts SQL 99 e aplicações JDBC em Java podem ser usados para criar
tabelas usadas em exercícios (é necessário configurá-los)
•As hierarquias de classes e diagramas de entidades a seguir poderão ser
usados nos exercícios (que irão fazer referência a eles)
19. JAVAEE7 Exercícios: Hierarquia e SQL 1
CREATE TABLE Livro(
id INTEGER PRIMARY KEY,
isbn VARCHAR(13) UNIQUE,
titulo VARCHAR(256),
idioma CHAR(2)
)
Livro
id: int
titulo: String
isbn: String
idioma: String
20. JAVAEE7 Exercícios: Hierarquia e SQL 2
Movie
id: int
title: String
imdb: String
director: Director
year: int
duration: int
Director
id: int
name: String
CREATE TABLE Diretor (
id INTEGER PRIMARY KEY,
nome VARCHAR(256),
)
CREATE TABLE Filme (
id INTEGER PRIMARY KEY,
imdb CHAR(9) NOT NULL,
titulo VARCHAR(256),
diretor INTEGER,
ano INTEGER,
duracao INTEGER,
constraint fk_diretor
foreign key (diretor)
references Diretor(id)
)
*
21. JAVAEE7 Exercícios: Hierarquia 3
1
1..*
1
1
*
PK
FK
Cliente
nome
email
pedidos
cartao
Usuario
id
senha
userid
Produto
id
nome
preco
Pedido
id
cliente
status
Item
id
pedido
produto
quantidade
Operador
codigo
versao
24. JAVAEE7 Exercícios: Hierarquia 4
Corrida
Etapa
Participante Ingressoingresso
participante
Localidade
participantes
etapas
origem
destino
valor
status
nome
nome
nome
nome
*
*
1
1
1
*
etapas
1
1
25. JAVAEE7 Exercícios:
Hierarquia 5
Livro
id: int
titulo: String
isbn: String
idioma: String
Autor
id: int
nome: String
Editora
id: int
nome: String
Assunto
id: AssuntoPK
descricao: String
Exemplar
id: int
disponivel: boolean
ExemplarEletronico
id: int
tamanho: long
ExemplarImpresso
id: int
paginas: int
Usuario
id: int
nome: String
emprestimos
usuario
livro
assunto
editora
autores
obras
titulos
contexto
assuntos 10
0..1
*
1
*
*
1
*
1
*
1
*
27. JAVAEE7 Configuração standalone
•Usada em projetos Java EE para rodar testes de integração com o banco ou em
aplicações JPA standalone
•É preciso incluir explicitamente no Classpath os JARs da API, do provedor JPA e
driver do banco de dados (e pool de conexões se houver)
<dependencies>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>javax.persistence</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>eclipselink</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.4.1208</version>
<scope>runtime</scope>
</dependency>
</dependencies>
Configuração das
dependências Maven para
para acesso JPA usando
EclipseLink 2.5 e banco de
dados PostgreSQL
28. JAVAEE7 Configuração standalone
•Como as transações serão controladas pela aplicação e serão locais, a unidade
de persistência declara o tipo de transações como RESOURCE_LOCAL:
<persistence version="2.1"…>
<persistence-unit name="tutorial-jpa"
transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>br.com.argonavis.javaee7.jpa.intro.Livro</class>
<properties>
…
<property name="eclipselink.ddl-generation"
value="drop-and-create-tables" />
</properties>
</persistence-unit>
</persistence>
Entidade a ser
configurada
Configuração do EclipseLink para gerar
tabelas automaticamente
(o Hibernate suporta uma equivalente)
29. JAVAEE7 Criação do EntityManager (standalone)
•A fábrica que cria o EntityManager pode ser obtida via consulta JNDI
global a um servidor (se houver container cliente configurado), ou criada
localmente com um objeto Persistence.
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
public class LivroTest {
@Test
public void testPersist() throws Exception {
EntityManagerFactory factory =
Persistence.createEntityManagerFactory("tutorial-jpa");
EntityManager em = factory.createEntityManager();
...
em.close();
}
}
Criação local do
EntityManager
Configuração para usar a
unidade de persistência
declarada no persistence.xml
30. JAVAEE7 Criação da entidade
•A entidade a ser sincronizada com a tabela é instanciada e
configurada normalmente em Java puro
public class LivroTest {
@Test
public void testPersist() throws Exception {
...
Livro livro = new Livro();
livro.setTitulo("Meu Livro");
livro.setPaginas(100);
...
}
}
31. JAVAEE7 Uso da persistência (standalone)
•Padrão básico: obter o contexto de persistência (createEntityManager)
seguido do contexto transacional (begin), realizar as operações envolvendo
persistência, e fechar o contexto transacional (commit se sucesso ou rollback
se falha, fechando o contexto de persistência (close) no final da operação.
public class LivroTest {
@Test
public void testPersist() throws Exception {
...
try {
em.getTransaction().begin();
em.persist(livro); // cria um registro novo no commit()
em.getTransaction().commit();
} catch (Exception e) {
em.getTransaction().rollback();
} finally {
em.close();
}
}
} // detalhes do teste (asserts) omitidos
É preciso obter o
contexto transacional
do EntityManager:
persist() precisa ser
chamada dentro de um
contexto transacional
32. JAVAEE7 Configuração em Java EE
•Em Java EE o suporte a JPA é nativo, e o acesso ao banco de dados
deve ocorrer através de um datasource previamente configurado
no servidor, acessível dentro de um contexto JTA
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
...
</dependencies>
Dependências em um projeto
Maven não são incluídas no
componente (JAR): o driver do
banco, pool e provedor JPA
fazem parte do servidor
33. JAVAEE7 Configuração em Java EE
•O arquivo persistence.xml declara estratégia transacional JTA
(gerenciada pelo container) e em vez de dados para acesso ao
banco, informa o caminho JNDI para o datasource correspondente
<persistence version="2.1" …>
<persistence-unit name="tutorial-jpa" transaction-type="JTA">
<jta-data-source>jdbc/TutorialJPA</jta-data-source>
<class>br.com.argonavis.javaee7.jpa.intro.ejb.Livro</class>
</persistence-unit>
</persistence>
34. JAVAEE7 Acesso via WebServlet
•Em Java EE o EntityManager poderá ser obtido via JNDI injeção de dependências
com de @PersistenceContext (ou via CDI, se previamente configurado)
@WebServlet("/Livro") public class LivroTestServlet extends HttpServlet {
@PersistenceContext(unitName="tutorial-jpa")
EntityManager em;
@Resource
UserTransaction ut;
protected void doGet(HttpServletRequest rq, HttpServletResponse rs)
throws ServletException, IOException {
Writer out = rs.getWriter();
try {
ut.begin();
List<Livro> livros =
em.createQuery("select livro from Livro livro").getResultList();
for(Livro livro : livros)
out.write("<p>"+livro.getTitulo()+ "</p>");
ut.commit();
} catch (…) {…} // rollback e finalização
}
UserTransaction é necessário para
delimitar o contexto transacional
35. JAVAEE7
@Stateless
public class LivroDAOSessionBean {
@PersistenceContext
EntityManager em;
public Livro findByID(Long id) {
Query query = em.createNamedQuery("selectById");
query.setParameter("id", id);
return (Livro)query.getSingleResult();
}
public List<Livro> findAll() {
return (List<Livro>)em.createNamedQuery("selectAll")
.getResultList();
}
public void delete(Livro livro) { em.remove(livro); }
public void update(Livro livro) { em.merge(livro); }
public Livro insert(Livro livro) { return em.merge(livro); }
}
Acesso via EJB
Se houver apenas uma unidade
de persistência, o nome dela não
precisa ser informado
Os métodos de um Session Bean são
transacionais por default. Haverá
rollback se houver exceção
36. JAVAEE7 Named queries
•Um @NamedQuery contém JPQL associado a um nome que
pode ser usado pelos clientes para obter referência a um query:
•Exemplo:
@Entity
@NamedQueries({
@NamedQuery(name="selectAll",
query="SELECT livro FROM Livro livro"),
@NamedQuery(name="selectById",
query="SELECT livro FROM Livro livro WHERE livro.id=:id")
})
public class Livro { ... }
TypedQuery<Livro> query =
em.createNamedQuery("selectById", Livro.class);
37. JAVAEE7 Acesso via CDI
•Métodos não são transacionais por default. O contexto transacional pode
ser obtido de duas formas:
•UserTransaction (como no WebServlet)
•Anotação @Transactional (comportamento igual ao EJB)
@Named public class LivroDAOManagedBean {
@Inject EntityManager em;
public Livro findByID(Long id) {
TypedQuery<Livro> query =
em.createNamedQuery("selectById", Livro.class);
query.setParameter("id", id);
return query.getSingleResult();
}
public List<Livro> findAll() {
return em.createNamedQuery("selectAll", Livro.class)
.getResultList();
}
@Transactional public void delete(Livro livro) { em.remove(livro); }
@Transactional public void update(Livro livro) { em.merge(livro); }
@Transactional public Livro insert(Livro livro) { return em.merge(livro); }
}
38. JAVAEE7 Passo-a-passo JPA: configuração
•Crie um projeto (Maven, Eclipse, NetBeans, etc)
•Configure dependências necessárias para compilar JPA (e executar se standalone)
•Configure um banco de dados no seu projeto (se standalone) ou no servidor de
aplicações (se Java EE) e use as propriedades para configurar o persistence.xml
•Inclua dados de configuração em META-INF/persistence.xml definindo um nome para
a unidade de persistência, e tipo RESOURCE_LOCAL, se standalone, e JTA, se Java EE
<persistence version="2.1" ... >
<persistence-unit name="biblioteca-PU" transaction-type="JTA">
<jta-data-source>jdbc/sample</jta-data-source>
<class>biblioteca.Livro</class>
<properties>
<property name="javax.persistence.schema-generation.database.action"
value="drop-and-create"/>
</properties>
</persistence-unit>
</persistence>
Use para gerar as tabelas
automaticamente
Entidade mapeada
39. JAVAEE7 Passo-a-passo JPA: mapeamento
•Crie um POJO (classe Java com atributos privativos acessíveis via getters e setters)
•Declare @Entity, @Id e uma estratégia de geração do ID (depende do banco de dados)
•Inclua uma referência à entidade no persistence.xml (veja slide anterior)
@Entity
public class Livro {
@Id @GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String titulo;
private String isbn;
private String idioma;
public int getId() {return id;}
public void setId(int id) {this.id = id;}
public String getTitulo() {return titulo;}
...
}
Gera ID automaticamente
(depende do banco usado)
40. JAVAEE7 Passo-a-passo JPA: persistência
•Crie um cliente Java. Em Java EE use um WebServlet, ManagedBean
(exemplo abaixo) ou Session Bean
•Obtenha uma referência para o EntityManager da unidade de persistência
•Chame os métodos de EntityManager dentro de contexto transacional
(automático em Session Beans)
@Named @RequestScoped
public class LivroBean {
@PersistenceContext(unitName="biblioteca-PU")
EntityManager em;
@Override @Transactional
public Collection<Livro> getLivros() {
String query = "select livro from Livro livro";
return em.createQuery(query, Livro.class).getResultList();
}
@Override @Transactional
public int insert(Livro livro) {
em.persist(livro);
return em.merge(livro).getId();
} ...
<h:dataTable value="#{livroBean.livros}"
var="livro">
<h:column>#{livro.isbn}</h:column>
<h:column>#{livro.titulo}</h:column>
<h:column>#{livro.idioma}</h:column>
Cliente para o
ManagedBean
41. JAVAEE7 Exercícios
•1. Configure seu ambiente para usar JPA (persistence.xml)
•a.Ambiente standalone: carregue as dependências necessárias no ClassPath,
configure um banco de dados e um provedor
•b.Ambiente Java EE: configure um datasource JTA no servidor de aplicações e um
provedor de persistência
•2. Construa a entidade da Hierarquia 1 (classe Livro)
•Não crie uma tabela em SQL: configure para que as tabelas do banco sejam geradas
automaticamente) e chame métodos para inserir e selecionar objetos
•Depois de configurado, rode a classe PopulateLivros para preencher a tabela com
dados que possam ser pesquisados
43. JAVAEE7 Operações CRUD
•Create, Retrieve, Update, Delete (Criar, Recuperar,Atualizar,
Remover) são as principais tarefas da camada de persistência
•Podem envolver uma entidade ou uma rede de entidades
interligadas através de relacionamentos
•JPA oferece várias maneiras de realizar operações CRUD:
•Métodos de persistência transitiva: persist, merge, delete, find
•Java Persistence Query Language (JPQL)
•API Criteria
•SQL nativo
44. JAVAEE7 Retrieve com JPQL
•A obtenção de uma entidade, o “R” de “Retrieve” do acrônimo CRUD,
pode ser feita conhecendo-se sua chave primária através de find:
Livro livro = em.find(Livro.class, 1234);
•Outra alternativa é realizar uma consulta pesquisando o estado da
entidade com JPQL através de um Query (ou TypedQuery):
TypedQuery query =
em.createQuery("select m from Livro m where m.id=:id",
Livro.class);
query.setParameter("id", 1234);
Livro livro = query.getSingleResult();
45. JAVAEE7 Retrieve com Criteria
•Criteria é um query construído dinamicamente via relacionamentos entre
objetos. É ideal para pesquisas que são construídas em tempo de execução:
•Para usar a API desta forma, a classe Livro_class foi gerada automaticamente
(veja: Criteria Metamodel API) pelas ferramentas da IDE (plug-in Maven)
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Livro> criteria = builder.createQuery(Livro.class);
Root<Livro> queryRoot = criteria.from(Livro.class);
criteria.where(builder.equal(queryRoot.get(Livro_.id), 1234));
TypedQuery<Livro> q = em.createQuery(criteria);
Livro livro = q.getSingleResult();
46. JAVAEE7 Create-Update-Delete com find e JPQL
•Operações que envolvem atualização, “Create”, “Update” e “Delete”,
geralmente são executadas através de métodos de persistência
transitiva persist, merge e delete
•A mesma alteração também pode ser realizada via JPQL:
Query query = em.createQuery("UPDATE Livro m"
+ "SET m.titulo = :titulo WHERE m.id = :id");
query.setParameter("titulo", "Novo título");
query.setParameter("id", "1234");
query.executeUpdate();
Livro livro = em.find(Livro.class, 1234);
livro.setTitulo("Novo título");
em.merge(livro);
47. JAVAEE7 Create-Update-Delete com Criteria
•As mesmas operações podem ser realizadas com a API Criteria
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaUpdate updateCriteria =
builder.createCriteriaUpdate(Livro.class);
Root<Livro> updateRoot =
updateCriteria.from(Livro.class);
updateCriteria.where(builder.equal(updateRoot.get(Livro_.id),
"1234"));
updateCriteria.set(updateRoot.get(Livro_.titulo),
"Novo título");
Query q = em.createQuery(updateCriteria);
q.executeUpdate();
48. JAVAEE7 Operações de persistência
•Uma entidade pode estar em quatro estados durante seu ciclo de vida
•transiente (new) - @Id = null
•persistente (managed) - @Id != null e sincronizado com tabela
•desligado (detached) - @Id != null e não sincronizado (pode estar obsoleto)
•removido (removed) - @Id != null mas registro inexistente na tabela
•O estado determina o comportamento do objeto quando são chamadas
operações de persistência transitiva sobre ele
•Entidade nova (transiente) ainda não inserida no banco tem ID null
•Quando a entidade torna-se persistente ela recebe um ID. Uma vez
atribuído um ID a uma entidade, esse valor não muda mais
50. JAVAEE7 Controle da persistência
•void persist(objeto) – Gera SQL INSERT e transforma o objeto em uma entidade
persistente. Causa EntityExistsException se o ID já existir no banco.
•Object merge(objeto) – Gera SQL UPDATE ou INSERT e devolve a entidade construída. O
INSERT será gerado se o ID do objeto for null. Caso contrário, o ID será chave primária para
realizar UPDATE. Causa IllegalArgumentException se ID não for null e não existir no banco.
•void remove(objeto) – Remove o registro do banco, desligando a entidade
permanentemente.
•void detach(objeto) – Separa a instancia do seu registro tornando a entidade desligada
(detached). Ocorre em entidades usadas fora do contexto transacional. Um merge() ou
refresh() dentro do contexto transacional religa a entidade tornando-a persistente.
•void refresh(objeto) – Sincroniza a entidade com dados obtidos do banco, sobrepondo
alterações que tenham sido feitas no estado do objeto.
•void flush() – Sincroniza as entidades do contexto de persistência (oposto de refresh).
52. JAVAEE7 Callbacks
•@PostLoad - executado depois que uma entidade foi carregada no contexto
de persistência atual (ou refresh)
•@PrePersist - executado antes da operação persist ser executada (ou
cascade - sincronizado com persist se entidade for sincronizada via merge)
•@PostPersist - executado depois que entidade foi persistida (inclusive via
cascade)
•@PreUpdate - antes de operação UPDATE
•@PostUpdate - depois de operação UPDATE
•@PreRemove - antes de uma operação de remoção
53. JAVAEE7 EntityListener
•Para declarar callbacks em uma classe separada, pode-se usar a anotação
@EntityListener na entidade informando o nome da classe que os contém:
•Neste caso, os métodos devem receber a entidade como parâmetro:
@Entity
@EntityListeners(LivroListener.class)
public class Livro implements Serializable {
…
}
public class LivroListener {
@PostLoad
public void livroLoaded(Livro livro) { ... }
...
}
54. JAVAEE7 Exercícios
•3. Escreva um cliente JPA simples para
•a) Inserir uma lista de livros no banco de dados (converta o código de
PopulateLivros.java para JPA)
•b) Listar os livros
•c) Alterar um dos livros (trocar o título)
•d) Remover um dos livros
•e) Recuperar um dos livros pela chave primária
•f) Listar a coleção de objetos resultante
•4. Execute a classe PopulateFilmes.java (que cria a Hierarquia 2) e escreva
um cliente que liste todos os filmes e diretores
56. JAVAEE7 Mapeamento de entidades
•Uma classe que representa uma entidade
• deve ser anotada com @Entity (ou declarada como <entity> no
deployment descriptor orm.xml)
• deve ter um campo @Id (a menos que participe como subclasse de uma
hierarquia mapeada em JPA)
• se não usar os valores default, deve declarar mapeamentos explícitos
para tabelas e colunas (@Table, @Column, @JoinColumn)
•Deve ter um construtor sem argumentos e não pode ser interface
ou um enum ou classe final, nem ter variáveis de instância ou
métodos finais
57. JAVAEE7 Mapeamento de 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
•O estado persistente é representado pelas variáveis de instância
• Acessadas via propriedades (getters/setters)
• Por default, variáveis de instância não declaradas transient e que
não tenham a anotação @Transient são consideradas persistentes
58. JAVAEE7 Mapeamento de 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>)
•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
59. JAVAEE7 Mapeamento com defaults
@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)
Anotações também possuem
propriedades configuráveis;
se omitidas, são usadas as default
60. JAVAEE7 Mapeamento detalhado
@Entity @Table(name="CLIENTES", schema="loja")
public class Cliente implements Serializable {
@Id @GeneratedValue(strategy=SEQUENCE, generator="CLI_SEQ")
@Column(name="CUST_ID")
private Long id;
@Basic @Column(name="NOME_CL", nullable=false, length=512)
private String nome;
@Basic @Column(name="END_CL", nullable=false, length=512)
private Endereco endereco;
@OneToMany(Cascade={CascadeType.ALL}, mappedBy="cliente", fetch=FetchType.EAGER)
@JoinColumn(name="CLIENTE_ID", nullable=false)
private Set<Pedido> pedidos = new HashSet();
@ManyToMany(mappedBy="clientes") @OrderBy("valor ASC")
@JoinTable(name="CLIENTES_PAGAMENTOS",
joinColumns= @JoinColumn(name="CLIENTE_ID", referencedColumnName="ID"),
inverseJoinColumns= @JoinColumn(name="PAG_ID", referencedColumnName="ID"))
private Set<Pagamento> formasPagamento = new HashSet();
...
}
Anotações detalhando forma como
entidade é mapeada às tabelas, e
selecionando configurações
default para gravação e
recuperação de objetos
61. JAVAEE7
•5. Configure um mapeamento entre a tabela Livro e a classe Livro
•6. Configure um mapeamento entre a classe Movie e a tabela Filme
Exercícios
CREATE TABLE Livro(
id INTEGER PRIMARY KEY,
isbn VARCHAR(13) UNIQUE,
titulo VARCHAR(256),
idioma CHAR(2)
)
CREATE TABLE Filme (
id INTEGER PRIMARY KEY,
imdb CHAR(9) NOT NULL,
titulo VARCHAR(256),
diretor VARCHAR(64),
ano INTEGER,
duracao INTEGER
)
Livro
id: int
titulo: String
isbn: String
idioma: String
Movie
id: int
title: String
imdb: String
director: Director
year: int
duration: int
63. JAVAEE7 Relacionamentos entre entidades
•Para mapear relacionamentos,JPA usa informações da estrutura do
código Java e anotações mínimas (serão usados defaults onde possível)
•A cardinalidade define o número de entidades em cada lado. Podem ser
mapeados “muitos” e “um” resultando em quatro possibilidades:
•Um para muitos
•Muitos para um
•Um para um
•Muitos para muitos.
•As duas primeiras são equivalentes, portanto há três associações:
•@ManyToOne / @OneToMany
•@OneToOne
•@ManyToMany
64. JAVAEE7 @ManyToOne unidirecional
@Entity
public class Produto {
@Id @GeneratedValue
int codigo;
String nome;
double preco;
public int getCodigo() {
return codigo;
}
public void setCodigo(int codigo) {
this.codigo = codigo;
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
...
@Entity
public class Item {
@ManyToOne
private Produto produto;
public Produto getProduto() {
return produto;
}
public void setProduto(Produto produto) {
this.produto = produto;
}
public double subtotal() {
return produto.getPreco() * quantidade;
}
...
Classe Produto não sabe da
existência da associação*
Método de negócio que
utiliza-se da associação para
realizar uma operação
* Essas anotações são insuficientes para a geração automática do esquema (as tabelas devem existir antes)
65. JAVAEE7 Associações bidirecionais
•Se as anotações @OneToOne, @ManyToMany e @ManyToOne forem
usadas em associações bidirecionais é necessário especificar anotações de
ambos os lados e utilizar o atributo mappedBy para informar o atributo da
outra classe que faz a associação
•O diagrama abaixo ilustra algumas associações bidirecionais.
@OneToOne
CPF cpf;
@OneToOne(mappedBy="cpf")
Cliente cliente;
@ManyToMany
Set<Turma> turmas;
@ManyToMany(mappedBy="turmas")
Set<Aluno> alunos;
@ManyToOne
Item item;
@OneToMany(mappedBy="item")
Set<Produto> produtos;
66. JAVAEE7 @ManyToOne bidirecional
@Entity
public class Pedido {
@OneToMany(mappedBy="pedido")
private Set<Item> itens = new HashSet<Item>();
public Set<Item> getItens() {
return itens;
}
public void setItens(Set<Item> itens) {
this.itens = itens;
}
public void addItem(Item item) {
itens.add(item);
item.setPedido(this);
}
public double subtotal() {
double subtotal = 0;
for (Item item : itens) {
subtotal += item.subtotal();
}
return subtotal;
}
...
@Entity
public class Item {
@ManyToOne
private Pedido pedido;
public Pedido getPedido() {
return pedido;
}
public void setPedido(Pedido pedido) {
this.pedido = pedido;
}
public double subtotal() { ... }
...
Utilitário para facilitar a adição de itens na
associação (atualiza os dois lados da associação)
Método de negócio que utiliza-se da associação
para realizar uma operação
67. JAVAEE7 @ManyToMany bidirecional
@Entity
public class Aluno {
@Id @GeneratedValue
private int id;
@ManyToMany(mappedBy="alunos")
private Set<Turma> turmas;
public Set<Turma> getTurmas() {
return turmas;
}
public void setTurmas(Set<Turma> turmas) {
this.turmas = turmas;
}
public void matricular(Turma turma) {
turmas.add(turma);
if(!turmas.contains(turma))
turma.addAluno(this);
}
...
}
@Entity
public class Turma {
@Id @GeneratedValue
private int id;
@ManyToMany(mappedBy="turmas")
private Set<Aluno> alunos;
public Set<Aluno> getAlunos() {
return alunos;
}
public void setAlunos(Set<Turma> turmas) {
this.alunos = alunos;
}
public void addAluno(Aluno aluno) {
alunos.add(aluno);
if(!alunos.contains(aluno))
aluno.matricular(this);
}
...
}
Métodos utilitários para adicionar alunos e turmas
68. JAVAEE7 Relacionamentos um-para-um
•Objetos em um relacionamento com alto grau de dependência. No modelo
relacional isto pode ser implementado de três maneiras:
•Dois objetos, uma única tabela (dados em colunas da mesma tabela)
•Um objeto, duas tabelas (dados em colunas de outra tabela)
•Dois objetos, duas tabelas unidas por associação
•Em JPA, a terceira estratégia pode ser implementada usando @ManyToOne
(unidirecional com @UniqueConstraint) ou mais facilmente usando @OneToOne
(via chave estrangeira ou primária).
•As duas primeiras estratégias não são relacionamentos entre entidades, mas
associações com instâncias ou coleções. Em JPA, a primeira é implementada usando
@Embeddable e a segunda com @SecondaryTable.
69. JAVAEE7 @OneToOne bidirecional
@Entity
public class Cliente {
@OneToOne(mappedBy="cliente")
private CPF cpf;
public CPF getCPF() {
return cpf;
}
public void setCPF(CPF cpf) {
this.cpf = cpf;
}
...
@Entity
public class CPF {
@OneToOne
private Cliente cliente;
public Cliente getCliente() {
return cliente;
}
public void setCliente(Cliente
cliente) {
this.cliente = cliente;
}
...
Só é necessário informar
o mappedBy em um dos
lados da associação
70. JAVAEE7 Tipos das Coleções: List ou Set
•Relacionamentos @ManyToOne e @ManyToMany envolvem coleções que
normalmente são declarados como Set ou List
•O mapeamento natural para registros em uma tabela é java.util.Set,
porque Sets não possuem uma ordem especifica e não podem ser
duplicados
•Sets podem ser ordenados no query usando @OrderBy e na instância
usando uma implementação ordenada como TreeSet
•Pode-se usar List quando o índice é importante ou para compatibilidade
com outros componentes (ex: componentes Primefaces que esperam List)
71. JAVAEE7 Mapas
•E possível mapear uma coleção a um mapa usando @MapKey e representando
a coleção como um java.util.Map.
•Um Map contem um Set de chaves e uma Collection de valores, e é indicado
situações nas quais é mais eficiente recuperar um objeto através de uma chave.
•A chave default é a chave-primária dos objetos da coleção:
@Entity
public class LojaVirtual {
...
@OneToMany(mappedBy="lojaVirtual")
@MapKey // indexado pela chave primaria
public Map<Long, Cliente> clientes = new HashMap <>();
...
}
72. JAVAEE7 Mapas
•Usando o atributo name é possível indexar por outras propriedades dos
objetos da coleção:
•O mapeamento da chave também pode ser feito através das colunas, em vez
dos atributos, usando @MapKeyColumn e @MapKeyJoinColumn.
•Existem ainda mais duas anotações para @MapKey
•@MapKeyEnumerated, se a chave for uma enumeração
•@MapKeyTemporal, se a chave for uma data ou Timestamp.
@Entity
public class LojaVirtual {
...
@OneToMany(mappedBy="lojaVirtual")
@MapKey(name="cpf") // indexado pelo CPF
public Map<String, Cliente> clientes = new HashMap<>();
...
}
Veja mais detalhes
na documentação
73. JAVAEE7 Componentes @Embeddable
•Entidades são objetos persistentes que têm identidade no banco
•Objetos que contém dados persistentes mas não têm identidade unívoca
no banco são identificados pelo seu estado, ou valor (value objects).
•Em JPA são anotados como @Embeddable
•No modelo de domínio representam um objeto independente, mas no modelo
relacional são apenas algumas colunas de uma tabela.
•São componentes da entidade que possui a tabela e declarados como
atributos anotados como @Embedded
•Muitas associações 1-1 podem ser implementadas de maneira mais
eficiente como objetos embutidos.
74. JAVAEE7 Componentes @Embeddable
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
•Uma única tabela contém dados dos objetos Cliente e Endereco
•Endereco é um componente de Cliente, e declarado como @Embeddable
•Um Cliente tem dois Enderecos e cada associação é declarada como @Embedded
75. JAVAEE7 Componentes @Embeddable
@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
76. JAVAEE7 @Embeddable collections
•Se uma entidade contém uma coleção de componentes embutidos, ou tipos
básicos (que não são entidades) deve ser anotado com @ElementCollection
•A entidade abaixo contém uma coleção de objetos Endereco, que são
@Embeddable, e uma coleção de Strings:
@Entity
public class Representantes {
...
@ElementCollection
private Set<Endereco> enderecos = new HashSet<>();
@ElementCollection
private Set<String> emails = new HashSet<>();
...
}
77. JAVAEE7 Chaves compostas
•@Embeddable pode ser usado na criação de chaves compostas. Por
exemplo, uma chave-primária que consiste de dois campos do tipo String:
•Ela pode ser usada em uma entidade anotada com @EmbeddedId:
•É responsabilidade da aplicação gerenciar o estado da chave composta, para
que a persistência transitiva ocorra corretamente.
@Entity @Table(name = "DEPENDENCIA")
public class Dependencia {
@EmbeddedId private DependenciaId id;
// ...
}
@Embeddable
public class DependenciaId {
@Column(name = "APP_ID") private String appID;
@Column(name = "GROUP_ID") private String groupID;
//...
}
78. JAVAEE7 Mapeamento de enumerações
•Enumerações podem ser persistidas se marcadas com @Enumerated
public enum Regiao {
NORTE, NORDESTE, SUL, SUDESTE, CENTRO_OESTE;
}
•Exemplo:
@Entity
public class SalaDeCinema { ...
@Enumerated(EnumType.STRING)
private Regiao regiao;
•O EnumType default é ORDINAL: grava número no registro da tabela.
STRING é não é afetado pela ordem das constantes (grava texto na tabela).
79. JAVAEE7 Mapeamento de uma classe a duas tabelas
•Usa-se @SecondaryTable para construir uma entidade que obtém
seus dados de duas tabelas.
•No exemplo abaixo a entidade Usuario está mapeado a uma
tabela principal (USUARIOS) e uma tabela secundária (IMAGENS)
80. JAVAEE7 Mapeamento de uma classe a duas tabelas
•@Table é a tabela principal e @SecondaryTable a tabela secundária, que
indica coluna(s) contendo a chave primária usada(s) para realizar a junção.
•@PrimaryKeyJoinColumn contém: chave primária da tabela principal, chave
referenciada na tabela secundária (ou apenas o primeiro se forem iguais). O
atributo da segunda tabela informa a tabela e coluna a ser mapeada
@Entity
@Table(name="USUARIOS")
@SecondaryTable(name="IMAGENS",
pkJoinColumns = @PrimaryKeyJoinColumn(name="USUARIO_ID",
referencedColumnName="IMG_ID"))
public class Usuario implements Serializable {
...
@Id Column(name="USUARIO_ID")
private Long userId;
@Column(table="IMAGENS", name="URL_IMG", nullable=true)
private String avatar; ...
81. JAVAEE7 Transitividade da persistência
•Relacionamentos em JPA são implementados em Java puro, e não são
gerenciados pelo container por default.Alterações precisam ser sincronizadas:
Pedido p = new Pedido();
Item i = new Item();
p.addItem(i); // atualizando o pedido
item.setPedido(p); // atualizando o item (se bidirecional)
•As entidades precisam ser persistentes.Tentar adicionar uma entidade
transiente a uma entidade persistente causa TransientObjectException.
•persist() ou merge() dentro de um contexto transacional resolve o problema:
em.persist(p);
em.persist(i);
82. JAVAEE7 Transitividade da persistência
•Pode-se configurar persistência transitiva no JPA usando o atributo
cascade das anotações de relacionamentos.
•Para propagar a inserção de entidades use CascadeType.PERSIST:
@OneToMany(cascade={CascadeType.PERSIST},
mappedBy="item" )
private Set<Item> itens = new HashSet<Item>();
•Quando um Pedido for persistido no banco, itens que estiverem na
hierarquia de objetos também serão persistidos automaticamente.
83. JAVAEE7 CascadeType
•O atributo cascade recebe um array de opções CascadeType
•PERSIST – propaga operações persist() e merge() em objetos novos
•MERGE – propaga operações merge()
•REMOVE – propaga operações remove()
•DETACH – propaga operações detach()
•REFRESH – propaga operações refresh()
•ALL – propaga todas as operações. Declarar cascade=ALL é o mesmo que
declarar cascade={PERSIST, MERGE, REMOVE, REFRESH, DETACH}
•Um uso típico é PERSIST + MERGE, que garante inserts e updates transitivos
@OneToMany(cascade={CascadeType.PERSIST, CascadeType.MERGE})
•Se a remoção de um Pedido exigir a remoção de seus Itens um REMOVE é
necessário. Pode-se usar ALL para que todas as operações sejam transitivas
84. JAVAEE7 Lazy loading
•Lazy loading um padrão de design usado para adiar a inicialização de um
objeto para quando ele for realmente necessário
•ORMs usam estratégias de lazy loading para otimizar pesquisas
•Contribui para a eficiência da aplicação se usado corretamente
•Um query, por default, retorna uma coleção de proxies para seus elementos (e
não a coleção preenchida pelos próprios elementos)
•Se um cliente fora do contexto transacional tentar acessar um elemento da
coleção, haverá uma exceção de inicialização lazy. O comportamento default
depende do provedor usado
•Hibernate lança LazyInitializationException.
•EclipseLink captura a exceção e abre nova conexão ao banco para obter os dados
85. JAVAEE7 Configuração de lazy loading
•É possível controlar o lazy loading configurando a recuperação default
como eager (ansiosa), em vez de lazy (preguiçosa) de duas maneiras:
•Através de mapeamento (estabelece um default para todas as situações)
•Através de instruções no query (a cada consulta)
•O atributo fetch=FetchType das anotações de relacionamentos configura o
modo eager como default para todas as operações
@OneToMany(mappedBy="pedido", fetch=FetchType.EAGER)
private Set<Item> itens = new HashSet<Item>();
•No query, um fetch join devolve a coleção com todos os seus elementos
select p from Pedido p join fetch p.itens
86. JAVAEE7 Exercícios
•7. Implemente o mapeamento Movie-Director da Hierarquia 2
•8. Implemente os mapeamentos de Cliente-Pedido-Item-Produto na
Hierarquia 3
•9. Implemente todos os mapeamentos da Hierarquia 4
•Use @Embedded para o relacionamento Etapa-Localidade
•Use @OneToOne para o relacionamento Participante-Ingresso
•Implemente uma enumeração para guardar o status do ingresso com as
constantes PAGO e NAO_PAGO
•O relacionamento Corrida-Etapa é unidirecional
•10. Implemente o relacionamento Assunto-Assunto na Hierarquia 35
88. JAVAEE7 Mapeamento de herança
•Entidades são objetos e suportam herança de classes, associações e
queries polimórficos. Podem ser abstratas ou concretas, e podem ser
subclasses ou superclasses de classes que não são entidades.
•Mas não existe herança em tabelas. Herança é o descasamento mais
visível entre os mundos relacional e orientado a objetos.
•O mundo OO possui relacionamento “é um” e “tem um”, enquanto que o
mundo relacional apenas possui relacionamento “tem um”.
•Há três estratégias para lidar com esse problema (sugeridas por Scott
Ambler, 2002). Essas estratégias são adotadas na arquitetura do JPA.
89. JAVAEE7 MappedSuperclass
•Entidades podem ter superclasses que não são entidades
•Se a superclasse possuir a anotação @MappedSuperclass, seus atributos serão
herdados e farão parte do estado persistente das subclasses
•Uma @MappedSuperclass também pode incluir anotações de persistência nos
seus atributos, mas não é uma entidade e não pode ser usada com operações de
EntityManager ou Query (não está mapeada a uma tabela)
•Apenas as subclasses de entidades concretas são mapeadas a tabelas e podem
conter colunas correspondentes aos atributos herdados.
@MappedSuperclass
public class Quantia {
@Id protected Long id;
protected BigDecimal valor; ...
}
@Entity public class Pagamento extends Quantia { ... }
@Entity public class Divida extends Quantia { ... }
90. JAVAEE7 @Inheritance e InheritanceType
•A anotação javax.persistence.Inheritance é usada para configurar o
mapeamento de herança entre entidades em JPA
•Elas implementam as três estratégias citadas que são selecionadas através
das constantes da enumeração InheritanceType:
public enum InheritanceType {
SINGLE_TABLE,
JOINED,
TABLE_PER_CLASS
};
•A estratégia default é SINGLE_TABLE, que mapeia todas as classes de uma
hierarquia a uma única tabela no banco de dados
•Há muitas possibilidades de configuração (mostraremos as mais simples)
91. JAVAEE7 Tabela por Classe Concreta: TABLE_PER_CLASS
Pagamento
id
valor
Credito
numero
validade
<<Entidade>>
Debito
banco
conta
<<Entidade>>
@Id declarado apenas na superclasse
@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class Pagamento {
@Id @GeneratedValue private long id;
}
@Entity
public class Debito extends Pagamento {...}
ID NUMERO VALIDADE
5
ID BANCO CONTA
5
CREDITO
DEBITO
<<Entidade>>
92. JAVAEE7 Tabela por Classe Concreta: TABLE_PER_CLASS
•Tem como vantagem o mapeamento direto entre classe e tabela normalizada.
•A gravação é realizada via classe concreta, mas a pesquisa pode ser polimórfica,
via classe abstrata e com resultado que inclui as subclasses concretas
•Ex: filtrar Pagamentos por valor, sem precisar saber se Crédito ou Débito:
select p from Pagamento p where p.valor > 1000
•A desvantagem dessa estratégia é a complexidade e ineficiência dos queries
gerados. Exemplo de SQL gerado no Hibernate:
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
93. JAVAEE7 Tabela por hierarquia: SINGLE_TABLE
@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="TIPO")
public abstract class Pagamento {
@Id @GeneratedValue private long id;
}
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
PAGAMENTO
Coluna (@DiscriminatorColumn)
presente na tabela mas sem
mapeamento na classe
@Entity @DiscriminatorValue("deb")
public class Debito extends Pagamento {...}
Pagamento
id
valor
Credito
numero
validade
<<Entidade>>
Debito
banco
conta
<<Entidade>>
<<Entidade>>
94. JAVAEE7 Tabela por hierarquia: SINGLE_TABLE
•Mapeia uma hierarquia de classes inteira a uma tabela
•A tabela precisa ter colunas para todas as propriedades de todas as classes.
•A vantagem são pesquisas polimórficas mais eficientes, já que tudo acontece
em uma única tabela. Este é o SQL conceitual para um query polimórfico:
select ID, VALOR, BANCO, CONTA, NUMERO, VALIDADE
from PAGAMENTO
where VALOR > 1000
•Em query na classe concreta, o provedor de persistência poderá gerar este SQL:
select ID, VALOR, BANCO, CONTA, NUMERO, VALIDADE
from PAGAMENTO
where TIPO = 'deb' and VALOR > 1000
•A principal desvantagem dessa estratégia é que as tabelas não são normalizadas
95. JAVAEE7 Tabela por subclasse: JOINED
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
public abstract class Pagamento {
@Id @GeneratedValue private long id;
}
Pagamento
id
valor
Credito
id
numero
validade
ID NUMERO VALIDADE
5
<<Entidade>>
Debito
id
banco
conta
<<Entidade>>
ID BANCO CONTA
5
CREDITO
DEBITO
@Entity
public class Debito extends Pagamento {...}
ID VALOR
5
PAGAMENTO ID é PK e FK
(default)
<<Entidade>>
96. JAVAEE7 Tabela por subclasse: JOINED
•Uma Classe abstrata ou concreta = Uma Tabela distinta; relacionamento
através de equivalência de chave primária e estrangeira (default)
•Modelo relacional normalizado
•Evolução sem efeitos colaterais: novas classes e tabelas podem ser criadas sem
afetar existentes
•Aplicação de restrições de integridade mais simples
•Performance da pesquisa polimórfica ainda é baixa em hierarquias complexas
(usa dados de todas as tabelas envolvidas)
•Os provedores de persistência* geralmente geram SQL com outer join para
pesquisas polimórficas e inner join para pesquisas em classes concretas
*Hibernate e EclipseLink
97. JAVAEE7 Qual estratégia?
Há necessidade
de consultas
polimórficas?
Hierarquia de
classes é pequena?
Tabela por Hierarquia
InheritanceType.SINGLE_TABLE
Tabela por Subclasse
InheritanceType.JOINED
Tabela por Classe Concreta
InheritanceType.TABLE_PER_CLASS
Tabelas tem
que ser
normalizadas?
NÃO*
* Se houver suporte no provedor (o suporte é opcional)
SIM
SIM
NÃO
NÃO
SIM
(default)
98. JAVAEE7 Exercícios
•11. Implemente um mapeamento da hierarquia Usuario-
Operador-Cliente (Hierarquia 3) usando qualquer uma das três
estratégias
•12. Implemente uma mapeamento da hierarquia Exemplar-
Exemplar Eletrônico-Exemplar Impresso (Hierarquia 5) usando
uma estratégia diferente
100. JAVAEE7 Consultas (queries)
•Consultas podem ser construídas para recuperar entidades a
partir de critérios baseados em seu conteúdo
•JPA possui duas estratégias nativas:
•JPQL – linguagem similar a SQL baseada em comandos de texto
•Criteria – API usada para construir consultas usando objetos.
•JPQL é geralmente mais fácil de aprender, mas Criteria permite a
criação de queries com maior potencial de reuso e evolução, além
de ser recomendada para queries dinâmicas
101. JAVAEE7 Cláusulas
•Consultas em bases de objetos têm princípios similares a
consultas em bancos de dados
•A cláusula mais importante é FROM, que declara o escopo da
pesquisa. Na API Criteria é representada por um objeto raiz.
•A clausula SELECT indica o que está sendo selecionado dentro do
escopo FROM. Em JPA pode-se selecionar entidades, seus atributos, e
resultados de operações realizados sobre elas
•A cláusula opcional WHERE declara filtros que restringem os
resultados da pesquisa. Sem WHERE todos os elementos do escopo
declarado em FROM serão recuperados
102. JAVAEE7 Joins
•Operação que amplia o domínio da função para incluir objetos
de um relacionamento usadas em JPQL e Criteria
•Consultas que navegam no grafo de objetos usando o operador
ponto (ex: cliente.pedido.item) escondem joins implícitos
•Joins em grafos de objetos são similares mas não idênticos a
joins no mundo relacional. Há três tipos em JPA. :
•Lazy Inner Joins (default – joins implícitos são sempre inner joins)
•Lazy Outer (ou Left) Joins
•Eager (ou Fetch) Joins (que podem ser inner ou outer)
103. JAVAEE7 Inner Joins e Outer joins
•Inner Joins consideram apenas resultados que contém o
relacionamento
•Elementos que não possuem o relacionamento declarado são filtrados
•Exemplo: uma consulta sobre objetos Cliente para obter o total de
Pedidos (relacionamento Cliente-Pedido)
•Um inner join não retorna os clientes que não têm pedidos.
•Um outer join retorna tudo, mesmo que o cliente seja null
104. JAVAEE7 Lazy Joins e Eager Joins
•A inicialização dos elementos de uma coleção é Lazy por default
•Uma coleção obtida em uma consulta pode conter apenas um proxy
para cada elemento (é preciso fazer novas consultas para obter cada um)
•O comportamento exato é dependente do contexto transacional e
também do provedor de persistência utilizado
•Eager Joins garantem que a coleção seja inicializada antes do uso
•Depois da consulta a coleção está povoada com todos os seus elementos
•Isto pode ser pré-configurado no mapeamento, mas é mais eficiente
poder selecionar esse comportamento durante a consulta
105. JAVAEE7 JPQL (JPA Query Language)
•Linguagem de recuperação de dados
•Similar a linguagens de query de objetos (HQL, EJB-QL, etc.) que a precederam
•Parece com SQL mas opera sobre objetos e não tabelas
•Consultas são objetos da classe Query/TypedQuery e podem ser construídas
através de métodos de EntityManager
•As instruções do query podem ser passadas diretamente para o método
createQuery() como parâmetro do tipo String.
@PersistenceContext
EntityManager em;
...
TypedQuery query =
em.createQuery("SELECT p FROM Produto p", Produto.class);
106. JAVAEE7 Named queries
•O query pode também ser declarado previamente em anotações
@NamedQuery em cada entidade:
•Associado a um identificador, referenciado em createNamedQuery()
@Entity
@NamedQueries({
@NamedQuery(name="selectAllProdutos",
query="SELECT p FROM Produto p")
})
public class Produto implements Serializable { ... }
TypedQuery query =
em.createNamedQuery("selectAllProdutos", Produto.class);
107. JAVAEE7 Consultas parametrizadas
•Os parâmetros podem ser declarados em JPQL usando
identificadores numéricos ou nomes precedidos por “:”:
•Se houver parâmetros, eles podem ser preenchidos através de um
ou mais métodos setParameter() antes de executar o query.
TypedQuery query =
em.createQuery("SELECT p FROM Produto p WHERE p.preco > :maximo",
Produto.class);
query.setParameter("máximo", 1000);
108. JAVAEE7 Métodos que executam queries
•Retornam resultados
•Se o resultado for uma coleção, pode-se usar getResultList():
•Se retornar apenas um item (por exemplo, se o produto for selecionado
por ID ou um campo unívoco), pode-se usar getSingleResult():
•Há outros métodos de execução não fazem pesquisa mas são usados
para remoção ou atualização, como executeUpdate()
List<Produto> resultado = query.getResultList();
Produto produto = query.getSingleResult();
109. JAVAEE7 Sintaxe de uma operação SELECT
•A principal operação de JPQL é SELECT, que tem a seguinte sintaxe geral
cláusula_select cláusula_from
[cláusula_where] [cláusula_group_by] [cláusula_having] [cláusula_order_by]
SELECT p.nome
FROM Produto AS p
WHERE p.codigo = '123'
Define o contexto da pesquisa e declara
p como alias da entidade Produto
Seleciona valores no contexto da entidade p
Parâmetro
Restrição da seleção
SELECT p
FROM Produto p
WHERE p.nome = :n
Alias (AS é opcional)
Parâmetro de query
Seleção de entidades
(cláusulas opcionais)
110. JAVAEE7 Cláusula FROM
•A cláusula FROM define o escopo informando qual ou quais os objetos
que estão sendo pesquisados, e declara aliases usados no resto do query.
•Cada alias tem um identificador e um tipo.
•O identificador é qualquer palavra não-reservada e o tipo é o nome da
entidade identificada como @Entity.
•A palavra-chave AS (opcional) conecta o tipo ao identificador
•A cláusula FROM também pode selecionar múltiplos objetos e atributo e
incluir vários conectores de JOIN (inner join, left outer join).
... FROM Produto AS p
Tipo (Entidade) Identificador (alias)
111. JAVAEE7 Cláusula SELECT
•Informa o que se deseja obter. Pode retornar
•entidades,
•atributos dos objetos,
•resultados de expressões,
•tuplas envolvendo atributos, entidades e resultados de expressões
•Pode ser seguida de DISTINCT para eliminar valores duplicados
•SELECT utiliza o alias declarado na cláusula FROM:
SELECT p FROM Produto p
SELECT DISTINCT p FROM Produto p
SELECT p.codigo FROM Produto p
SELECT DISTINCT p.codigo FROM Produto p
112. JAVAEE7 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 identificados por número (?1, ?2) ou identificador (:nome, :item).
•Os literais usados nas pesquisas podem ser:
•Strings, representados entre apóstrofes: 'nome'
•Números, que têm mesmas regras de literais Java long e double
•Booleanos, representados por TRUE e FALSE (case-insensitive)
113. JAVAEE7 Operadores de WHERE
•Navegação .
•Expressões matemáticas +, -, *, /
•Expressões de comparação =, >, >=, <, <=, <>
•Operadores lógicos NOT,AND, OR
•Outros operadores: [NOT] BETWEEN, [NOT] IN, [NOT] LIKE, IS [NOT]
NULL, IS [NOT] EMPTY, [NOT] MEMBER
•O operador LIKE possui operadores adicionais:
•_ representa um único caractere
•% representa uma seqüência de zero ou mais caracteres
• caractere de escape (necessário para usar _ ou %) literalmente
114. JAVAEE7 Funções
•Funções podem aparecer em cláusulas WHERE ou SELECT:
•Manipulação de strings (WHERE): CONCAT, SUBSTRING,TRIM,
LOWER, UPPER, LEADING,TRAILING, BOTH, LENGTH, LOCATE
•Funções aritméticas (WHERE): ABS, SQRT, MOD, SIZE
•Data e hora (WHERE): CURRENT_DATE, CURRENT_TIME,
CURRENT_TIMESTAMP
•Funções de agregação (SELECT): COUNT, MAX, MIN,AVG, SUM
115. JAVAEE7 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
116. JAVAEE7 Exemplos
•Encontre todos os produtos cujo fabricante é Sun ou Intel
SELECT p
FROM Produto p
WHERE p.fabricante IN ('Intel', 'Sun')
•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'
117. JAVAEE7 Exemplos
•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
118. JAVAEE7 Exemplos
•Retorne os pedidos que contem um determinado item (passado como
parâmetro)
SELECT pedido
FROM Pedido pedido
WHERE :item IS MEMBER pedido.itens
•Encontre produtos com preços entre 1000 e 2000 ou que tenham código
1001
SELECT p
FROM Produto p
WHERE p.preco BETWEEN 1000 AND 2000 OR codigo = 1001
119. JAVAEE7 Exemplos com relacionamentos
•Selecione todos os clientes com pedidos que tenham total maior que
1000:
SELECT c
FROM Cliente c, IN(c.pedidos) p
WHERE p.total > 1000
•O mesmo query poderia ser escrito desta forma, expondo o join:
SELECT c
FROM Cliente c
INNER JOIN c.pedidos AS p
WHERE p.total > 1000
120. JAVAEE7 Exemplos com relacionamentos
•Lances onde o item de categoria iniciando em “Celular” tenha obtido lance >1000:
SELECT lance
FROM Lance lance
WHERE lance.item.categoria.nome LIKE 'Celular%'
AND lance.item.lanceObtido.total > 1000
•Queries equivalentes. O primeiro expõe um dos joins e o segundo expõe todos:
SELECT lance
FROM Lance lance
JOIN lance.item item
WHERE item.categoria.nome LIKE 'Celular%'
AND item.lanceObtido.total > 1000
SELECT lance
FROM Lance AS lance
JOIN lance.item AS item
JOIN item.categoria AS cat
JOIN item.lanceObtido AS lanceVencedor
WHERE cat.nome LIKE 'Celular%'
AND lanceVencedor.total > 1000
121. JAVAEE7 Exemplos usando funções
•Encontre a média do total de todos os pedidos:
SELECT AVG(pedido.total)
FROM Pedido pedido
•Obtenha a soma dos preços de todos os produtos dos pedidos feitos no
bairro de Botafogo:
SELECT SUM(item.produto.preco)
FROM Pedido pedido
JOIN pedido.itens item
JOIN pedido.cliente cliente
WHERE cliente.bairro = 'Botafogo'
AND cliente.cidade = 'Rio de Janeiro'
122. JAVAEE7 Exemplos usando funções
•Obtenha a contagem de clientes agrupadas por bairro:
SELECT cliente.bairro, COUNT(cliente)
FROM Cliente cliente
GROUP BY cliente.bairro
•Obtenha o valor médio dos pedidos, agrupados por pontos, para os
clientes que têm entre 1000 e 2000 pontos:
SELECT c.pontos, AVG(pedido.total)
FROM Pedido pedido
JOIN pedido.cliente c
GROUP BY c.pontos
HAVING c.pontos BETWEEN 1000 AND 2000
123. JAVAEE7 Exemplos com subqueries
•É possível usar os resultados de um query como parâmetro de
outro através de subqueries. O query abaixo usa como restrição o
resultado de um query que será testado com EXISTS.
•"Obtenha os empregados que são casados com outros
empregados"
SELECT DISTINCT emp
FROM Empregado emp
WHERE EXISTS (SELECT conjuge
FROM Empregado conjuge
WHERE conjuge = emp. conjuge)
124. JAVAEE7 Exemplos com subqueries: All/Any
•ALL e ANY são usados com subqueries
•ALL retorna true se todos os valores retornados forem true
•ANY só retorna true se resultado for vazio ou se todos os valores forem false
•"Retorne apenas os produtos cujo preço seja maior que o valor incluído
em todos os orçamentos"
SELECT produto
FROM Produto p
WHERE p.preco > ALL(SELECT o.item.preco
FROM Orcamento o
WHERE o.item.codigo = p.codigo)
125. JAVAEE7 Queries que retornam tuplas
•Queries que retornam múltiplos valores têm resultados
armazenados em arrays de objetos
• Cada índice do array corresponde respectivamente ao item selecionado,
na ordem em que é expresso em JPQL
•Exemplo: a query abaixo retorna três valores: um Long, um String e
um Diretor, que é uma entidade, respectivamente.
@Entity class Filme {
@Id private Long id;
private String imdb;
private String titulo;
@ManyToMany
private Diretor diretor;
...
}
SELECT f.id, f.titulo, d
FROM Filme f join f.diretores d
WHERE d.nome LIKE ‘%Allen’ @Entity class Diretor {
@Id private Long id;
private String nome;
...
}
126. JAVAEE7 Queries que retornam tuplas
•A execução so query irá retornar cada elemento como um array do tipo
Object[] onde cada valor será armazenado em um índice
•Se houver mais de um resultado, o tipo retornado será List<Object[]>
•Para obter os dados da query anterior pode-se usar:
List<Object[]> resultado =
(List<Object[]>)query.getResultAsList();
for(Object obj : resultado) {
Long id = (Long)obj[0];
String titulo = (String)obj[1];
Diretor diretor = (Diretor)obj[2];
...
}
127. JAVAEE7 Tuplas como DataTransferObject (DTO)
•Queries que retornam tuplas podem ter o objetivo de gerar
relatórios, ou para coletar dados para uma interface
•Os dados podem ser guardados em um objeto especialmente criado
para recebê-los:
package com.acme.filmes;
public class DataTransferObject {
private Long id;
private String titulo;
private Diretor diretor;
public DataTransferObject(Long id, String titulo, Diretor diretor) {
this.id = id;
this.titulo = titulo;
this.diretor = diretor;
}
...
}
128. JAVAEE7 Tuplas como DataTransferObject (DTO)
•O objeto pode então ser instanciado dentro do loop:
List<Object[]> resultado =
(List<Object[]>)query.getResultAsList();
for(Object obj : resultado) {
Long id = (Long)obj[0];
String titulo = (String)obj[1];
Diretor diretor = (Diretor)obj[2];
DataTransferObject dto =
new DataTransferObject(id, titulo, director);
// envia dto para algum lugar
}
129. JAVAEE7 Tuplas como DataTransferObject (DTO)
•Há uma sintaxe de SELECT que elimina a necessidade de escrever todo
esse código, chamando o construtor diretamente dentro do query:
•JPA 2.1 requer que o construtor use o nome qualificado da classe
•O query retorna uma List<DataTransferObject> em vez de
List<Object[]>, que pode ser retornada diretamente para o cliente:
SELECT new com.acme.filmes.DataTransferObject(f.id, f.titulo, d)
FROM Filme f join f.diretores d
WHERE d.nome LIKE ‘%Allen’
@Named public class ManagedBean {
public List<DataTransferObject> getDataToPopulateComponent() {
...
return query.getResultAsList();
} ...
}
130. JAVAEE7 Tuplas em JPA Criteria
•CriteriaQuery oferece outra alternativa de solução para este
problema com a interface javax.persistence.Tuple
•Os resultados ainda precisam ser extraídos individualmente, mas
são retornados em um objeto Tuple (em vez de Object[])
•O conteúdo de Tuple pode ser recuperado como uma List:
tupla.get(0), tupla.get(1), etc.
131. JAVAEE7 Named Queries
•Queries podem ser declarados em anotações e recuperadas pelo nome
•Isto, em geral é uma boa prática porque mantém todos os queries juntos, e
pode facilitar a manutenção deles
•Por outro lado, os mantém distante do código que cria os queries e
preenche os parâmetros, que pode dificultar o uso.
•Para declarar queries desta forma, use a anotação @NamedQueries:
@Entity
@NamedQueries({
@NamedQuery(name="produtoMaisBarato",
query="SELECT x FROM Produto x WHERE x.preco > ?1"),
@NamedQuery(name="produtoPorNome",
query="SELECT x FROM Produto x WHERE x.nome = :nomeParam")
})
public class Produto { ... }
132. JAVAEE7 Uso de Named Queries
•Para usar, chame o query pelo nome quando usar o EntityManager através
do método createNamedQuery(), e preencha seus parâmetros se houver:
EntityManager em = ...
Query q1 = em.createNamedQuery("produtoMaisBarato");
q1.setParameter(1, 10.0);
List<Produto> resultado1 = q1.getResultList();
Query q2 = em.createNamedQuery("produtoPorNome");
q2.setParameter("nomeParam", 10.0);
List<Produto> resultado2 = q2.getResultList();
133. JAVAEE7 Exercícios
•13. Escreva os seguintes queries em JPQL usando a Hierarquia 5:
•Todos os livros com idioma = PT ou EN
•Livros cujo título tenha a palavra "the" e id entre 2 e 10
•Livros que tenham um autor chamado "John"
•Autores que tenham mais de um livro
•Editoras que tenham mais de um título em idioma = EN
•14. Escreva os seguintes queries usando a Hierarquia 2:
•Todos os filmes com duração maior que 100 minutos
•Filmes produzidos entre 1950 e 1990
•Diretores com mais de 3 filmes
134. JAVAEE7 Exercícios
•15. Escreva os seguintes queries em JPQL usando a Hierarquia 4:
•Nomes de todos os Participantes da etapa com origem: "Nova York" e
destino: "Moscow" que possuem ingresso com status = "Pago"
•16. Escreva os seguintes queries usando a Hierarquia 3:
•Valor total (para cada item: item.quantidade * produto.preco) de
cada pedido de status="finalizado" do cliente de userid=Fred
•Soma (sum) e média (avg) dos valores totais de todos os pedidos
136. JAVAEE7 Criteria
•Criteria é uma API do JPA que permite a construção de queries
dinâmicos e verificados em tempo de compilação, usando objetos
•Este query JPQL
select p from Produto p where p.preco < 50.0
•Pode ser expresso em Criteria da seguinte forma:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Produto> query =
cb.createQuery(Produto.class); // tipo do select
Root<Produto> raiz = query.from(Produto.class); // tipo do from
Predicate condicao = cb.lessThan(raiz.get("preco"), 50.0); // predicado
query.where(condicao); // adiciona a clausula where
query.select(raiz); // diz o que vai ser selecionado
137. JAVAEE7 Construção de um query
•Queries de Criteria são um grafo de objetos
•A raiz do grafo é encapsulado no CriteriaQuery
•Um query mínimo requer o uso de três classes do pacote
javax.persistence.criteria:
•CriteriaBuilder, que encapsula vários métodos para construir o query,
•CriteriaQuery, que representa a consulta
•Root que é raiz da consulta e representa os objetos selecionados pela
cláusula FROM
138. JAVAEE7 Construção de um query
•1) Criar um CriteriaQuery através da classe CriteriaBuilder:
CriteriaBuilder builder =
entityManagerFactory.getCriteriaBuilder();
•2) obter o objeto que irá encapsular o query:
CriteriaQuery<Produto> query =
builder.createQuery( Produto.class );
•3) O objeto raiz do grafo é declarado através da interface Root, que constrói a
cláusula “from” do query. Isto é equivalente a fazer “from Produto p” em JPQL:
Root<Produto> p = query.from(Produto.class);
•4) Finalmente constrói-se a cláusula “select” do query usando o método select():
query.select(p);
•O query está pronto. Neste momento ele é equivalente ao string JPQL.
139. JAVAEE7 Execução de um Query
•Uma vez pronto, pode-se executar um Criteria Query da mesma
forma que um JPQL Query
•1) Passe o objeto query como parâmetro de um TypedQuery:
TypedQuery<Produto> query =
em.createQuery(query);
•2) Chame um método de execução pára obter os resultados
List<Produto> resultado =
query.getResultList();
140. JAVAEE7 Consultas que retornam tuplas
•Consultas que retornam múltiplos objetos podem ser feitas com
from(), join() ou fetch() e métodos similares
•O query JPQL:
SELECT p1, p2 FROM Produto p1, Produto p2
•Pode ser escrito usando Criteria da seguinte forma:
CriteriaQuery<Tuple> criteria = cb.createQuery(Tuple.class);
Root<Produto> p1 = criteria.from(Produto.class);
Root<Produto> p2 = criteria.from(Produto.class);
criteria.multiselect(p1, p2);
141. JAVAEE7 Outer Join (Left Join)
•Um join como na query abaixo:
SELECT item, produto.nome
FROM Item item
LEFT OUTER JOIN item.produto produto
•Pode ser escrito em Criteria da seguinte forma:
CriteriaQuery<Tuple> criteria = cb.createQuery(Tuple.class);
Root<Item> item = criteria.from(Item.class);
Join<Item> produto = item.join("produto", JoinType.LEFT);
criteria.multiselect(item, produto.get("nome"));
142. JAVAEE7 Fetch Join
•A interface Join retornada pelo método join() é subinterface da interface
From, portanto pode ser usada em metódos que esperam um From
•Um join usando fetch como este em JPQL:
SELECT item
FROM Item item
JOIN FETCH item.produto
•Pode ser expresso em Criteria usando a seguinte sintaxe:
CriteriaQuery<Item> criteria = cb.createQuery(Item.class);
Root<Item> item = criteria.from(Item.class);
Fetch<Item, Produto> produto = item.fetch("produto");
criteria.select(item);
144. JAVAEE7 Principais interfaces
•Expression – São construídas compondo outras expressões. Retornada em métodos de
CriteriaQuery e CriteriaBuilder (sum(), diff(), equal())
•Predicate – Conjunção (ou disjunção) das restrições de query representado por valor
booleano.A cláusula where recebe um Predicate. São construídos compondo outros
predicados. Retornado em métodos de CriteriaBuilder (equal(), and(), or(), between())
•Path – Representa caminho de navegação em um grafo. Ex: objeto.referencia.atributo.
O método get() de Root acessa atributos de objetos através de expressões de path.
•Join – Representa join para uma @Entity, objeto @Embeddable ou @Basic
•Selection – Qualquer item retornado em um resultado de query, como uma expressão
(Expression), subquery (SubQuery), predicado (Predicate), caminho (Path), etc.
•SubQuery – Representa um subquery
145. JAVAEE7 Métodos de CriteriaQuery e Subquery
•A hierarquia que representa um query em Criteria possui duas classes
•CriteriaQuery (query principal)
•Subquery
•Elas têm a superclasse AbstractQuery em comum e métodos que
representam cláusulas e outras partes de um query:
•Herdados de AbstractQuery (valem para queries principais e subqueries):
distinct(), from(), groupBy(), having(), subQuery(), where()
•Definidos em CriteriaQuery (valem apenas para queries principais):
multiselect(), select()
•Definidos em Subquery (valem apenas para subqueries):
correlate(), getParent()
146. JAVAEE7 CriteriaBuilder
•A classe CriteriaBuilder possui métodos para a construção de
todas as expressões usadas nas cláusulas dos queries e subqueries
•Encapsuladas em instâncias de Expression ou subclasses
•Podem ser criadas chamando os factory methods de CriteriaBuilder
•Envolvem desde a criação de literais até condicionais, expressões
boleanas, de comparação, etc.
Expression<Integer> i1 = builder.literal(123);
Expression<Integer> i2 = builder.sum(3,4);
Predicate p1 = builder.and(true, false);
Predicate p2 = builder.not(p1);
147. JAVAEE7 Exemplos
•Encontre todos os produtos que são chips e cuja margem de lucro é positiva
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Produto> query = cb.createQuery(Produto.class);
Root<Produto> root = query.from(Produto.class);
Predicate igual = cb.equal(root.get("descricao"), "chip");
Expression<Double> subtracao = cb.diff(root.get("preco"),
root.get("custo"));
Predicate maiorQue = cb.greaterThan(subtracao, 0.0);
Predicate clausulaWhere = cb.and(igual, maiorQue);
query.where(clausulaWhere);
query.select(root);
TypedQuery<Produto> q = em.createQuery(query);
148. JAVAEE7 Exemplos
•Encontre todos os produtos cujo preço é pelo menos 1000 e no máximo 2000
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Produto> query = cb.createQuery(Produto.class);
Root<Produto> root = query.from(Produto.class);
Predicate between = cb.between(root.get("preco"),
1000.0, 2000.0);
query.where(between);
query.select(root);
149. JAVAEE7 Exemplos
•Encontre todos os produtos cujo fabricante é Sun ou Intel
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Produto> query =
cb.createQuery(Produto.class);
Root<Produto> root = query.from(Produto.class);
query.where(root.get("fabricante").in("Intel", "Sun"));
query.select(root);
150. JAVAEE7 Exemplos
•Encontre todos os produtos com IDs que começam com 12 e
terminam em 3
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Produto> query =
cb.createQuery(Produto.class);
Root<Produto> root = query.from(Produto.class);
query.where(cb.like(root.get("id"), "12%3"));
query.select(root);
151. JAVAEE7 Exemplos
•Encontre todos os produtos que têm descrições null
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Produto> query =
cb.createQuery(Produto.class);
Root<Produto> root = query.from(Produto.class);
query.where(cb.isNull(root.get("descricao")));
query.select(root);
152. JAVAEE7 Exemplos
•Encontre todos os pedidos que não têm itens (coleção)
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Pedido> query =
cb.createQuery(Pedido.class);
Root<Pedido> root = query.from(Pedido.class);
query.where(cb.isEmpty(root.get("itens")));
query.select(root);
153. JAVAEE7 Exemplos
•Retorne os pedidos que contem um determinado item (passado
como parâmetro)
Item item = new Item(); // item a ser testado
// ...
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Pedido> query =
cb.createQuery(Pedido.class);
Root<Pedido> root = query.from(Pedido.class);
Predicate isMember =
cb.isMember(item, root.get("itens"));
query.where(isMember);
query.select(root);
154. JAVAEE7 Exemplos
•Encontre produtos com preços entre 1000 e 2000 ou que tenham código 1001
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Produto> query =
cb.createQuery(Produto.class);
Root<Produto> root = query.from(Produto.class);
Predicate between =
cb.between(root.get("preco"), 1000.0, 2000.0);
Predicate igual =
cb.equal(root.get("codigo"), 1001);
query.where(cb.or(between, igual));
query.select(root);
155. JAVAEE7 Exemplos com relacionamentos
•Selecione todos os clientes com pedidos com total maior que 1000:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Cliente> query = cb.createQuery(Cliente.class);
Root<Cliente> root = query.from(Cliente.class);
Path<Pedido> pedido = root.get("pedidos");
query.where(cb.greaterThan(pedido.get("total"), 1000.0));
query.select(root);
•Mesmo query usando um inner join explícito:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Cliente> query = cb.createQuery(Cliente.class);
Root<Cliente> root = query.from(Cliente.class);
Join<Cliente, Pedido> pedido = root.join("pedidos");
query.where(cb.greaterThan(pedido.get("total"), 1000.0));
query.select(root);
156. JAVAEE7 Exemplos com relacionamentos
•Lances onde o item é de categoria que começa com “Celular” e teve lance > 1000:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Lance> query = cb.createQuery(Lance.class);
Root<Lance> root = query.from(Lance.class);
query.where(cb.and(
cb.like(root.get("item").get("categoria").get("nome"), "Celular%"),
cb.greaterThan(root.get("item").get("lanceObtido").get("total")1000.0)));
query.select(root);
•Mesmo query usando vários inner joins explícitos:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Lance> query = cb.createQuery(Lance.class);
Root<Lance> root = query.from(Lance.class);
Join<Lance, Item> item = root.join("item");
Join<Item, Categoria> cat = item.join("categoria");
Join<Item, Lance> vencedor = item.join("lanceObtido");
query.where(cb.and(cb.like(cat.get("nome"), "Celular%"),
cb.greaterThan(vencedor.get("total"), 1000.0)));
query.select(root);
157. JAVAEE7 Exemplos
•Encontre a média do total de todos os pedidos:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Double> query =
cb.createQuery(Double.class); // avg retorna Double
Root<Pedido> root =
query.from(Pedido.class); // from Ingresso i
Expression<Double> media = cb.avg(root.get("total"));
query.select(media);
158. JAVAEE7 Exemplos
•Obtenha a soma dos preços de todos os produtos dos pedidos feitos
no bairro de Botafogo:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<BigDecimal> query = cb.createQuery(BigDecimal.class);
Root<Pedido> root = query.from(Pedido.class);
Join<Pedido, Item> item = root.join("itens");
Join<Pedido, Cliente> cliente = item.join("cliente");
Predicate where = cb.and(
cb.equal(cliente.get("bairro"), "Botafogo"),
cb.equal(cliente.get("cidade"), "Rio de Janeiro")
);
query.where(where);
Expression<BigDecimal> total =
cb.sum(item.get("produto").get("preco"));
query.select(total);
160. JAVAEE7 Exemplos
•Obtenha o valor médio dos pedidos, agrupados por pontos, para
os clientes que têm entre 1000 e 2000 pontos:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Object[]> query =
cb.createQuery(Object[].class);
Root<Pedido> root = query.from(Pedido.class);
query.multiselect(root.get("pontos"),
cb.avg(root.get("total")));
Join<Pedido, Cliente> cliente = root.join("cliente");
query.groupBy(cliente.get("pontos"));
query.having(cb.between(cliente.get("pontos"),
1000.0, 2000.0));
161. JAVAEE7 Exemplos com subqueries
•Obtenha os empregados que são casados com outros empregados:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Empregado> query =
cb.createQuery(Empregado.class);
Root<Empregado> root = query.from(Empregado.class);
Subquery<Empregado> subquery =
query.subquery(Empregado.class);
Root<Empregado> conjuge =
subquery.from(Empregado.class);
subquery.where(cb.equal(conjuge.get("conjuge "),
root.get("conjuge ")));
subquery.select(conjuge);
query.where(cb.exists(subquery));
query.select(root).distinct(true);
162. JAVAEE7 Exemplos com subqueries (All/Any)
•Retorne apenas os produtos cujo preço seja maior que o valor incluído
em todos os orçamentos:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Produto> query = cb.createQuery(Produto.class);
Root<Produto> root = query.from(Produto.class);
Subquery<Empregado> subquery =
query.subquery(Empregado.class);
Root<Orcamento> orcamento =
subquery.from(Orcamento.class);
subquery.where(cb.equal(orcamento.get("item ").get("codigo"),
root.get("codigo")));
subquery.select(orcamento.get("item").get("preco"));
query.where(cb.greaterThan(root.get("preco"),
cb.all(subquery)));
query.select(root);
163. JAVAEE7 Select com DataStransferObject
•O query usando select com construtor mostrado na seção anterior
com JPQL pode ser construído com Criteria da forma abaixo:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Lugares> query =
cb.createQuery(DataTransferObject.class);
Root<Filme> root = query.from(Filme.class);
Join<Filme, Diretor> diretor = root.join("etapas");
query.where(cb.like(root.get("nome"), "%Allen"));
query.select(cb.construct(Lugares.class,
etapa.get("origem").get("nome"),
etapa.get("destino").get("nome"))
);
164. JAVAEE7 Typesafe query
•É bem mais fácil encontrar erros em queries Criteria, comparados a JPQL, porque
o compilador ajuda na tarefa e detecta queries incorretos.
•Mas erros de digitação ainda podem acontecer já que a leitura dos campos das
entidades feita através de um get() recebe um String.
•Por exemplo, a linha abaixo para ler o campo “preco” não contém erros de
compilação:
Predicate condicao = qb.lt(raiz.get("prco"), 50.0);
•Mas se o string estiver errado, o query está incorreto: existem erros de sintaxe em
queries Criteria que também não são capturados em tempo de compilação!
165. JAVAEE7 Typesafe query
•A solução é o o typesafe query:
•O nome do atributo foi informado através de código Java, usando
um atributo estático e público da classe Produto_.
•Produto_.class é um metamodelo estático (static metamodel).
•Usando atributos através do metamodelo torna os queries typesafe
CriteriaBuilder qb = em.getCriteriaBuilder();
CriteriaQuery<Produto> cq = qb.createQuery(Produto.class);
Root<Produto> raiz = cq.from(Produto.class)
Predicate condicao = qb.lt(raiz.get(Produto_.preco), 50.0);
cq.where(condicao);
TypedQuery<Person> query = em.createQuery(cq);
166. JAVAEE7 Geração de metamodels
•Metamodelos precisam ser geradas. IDEs que suportam JPA 2 têm
ferramentas para gerá-los. Projetos Maven com EclipseLink podem
fazer a geração em uma das fases do POM.xml com um plug-in:
•O plugin precisa ser configurado e usará as classes declaradas no
persistence.xml para gerar metamodelos durante a fase de geração
de código do build (veja a documentação para mais detalhes)
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.jpa.modelgen.processor</artifactId>
<version>2.5.0</version>
</dependency>
167. JAVAEE7 Exercícios
•17. Implemente um dos queries do exercício 13 usando Criteria
•18. Implemente um dos queries do exercício 14 usando Criteria
•19. Implemente um dos queries do exercício 15 usando Criteria
•20. Implemente um dos queries do exercício 16 usando Criteria
•21. Implemente um bean que realize uma pesquisa dinâmica em
Livros, filtrando por autor, isbn e título. Crie um mecanismo que
permita construir um query dinâmico:
•Se nenhum campo estiver preenchido, a busca será feita sem filtros
•Se algum campo estiver preenchido, ele deve ser acrescentado na
cláusula where da pesquisa
169. JAVAEE7 Transações em JPA
•Podem ser
•Distribuídas com 2-phase commit (JTA)
•Configuradas como um recurso local (RESOURCE_LOCAL)
•Apenas transações JTA são gerenciadas pelo container (CMT - Container-
Managed Transactions) para configurar suporte transacional de forma
transparente e declarativa
•Transações JTA também podem ser controladas programaticamente (BMT -
Bean-Managed Transactions) através da API da classe UserTransaction,
que pode ser injetada como resource em componentes Java EE
170. JAVAEE7 Resource Local
•Usada em ambientes Java SE ou onde não há suporte a transações distribuídas
•getTransaction() obtém contexto transacional para operações de persistência
•Transações são delimitadas pela chamada dos métodos begin() e commit()/rollback():
public class AlunoDAO {
private EntityManagerFactory emf;
AlunoDAO() { emf = Persistence.createEntityManagerFactory("escola-PU");}
public void addAluno(Aluno aluno) {
EntityManager em = emf.getEntityManager();
try
em.getTransaction().begin();
em.persist(aluno);
em.getTransaction().commit();
} catch(Exception e) {
em.rollback();
} finally { em.close(); }
}
}
<persistence-unit
name="escola-PU"
transaction-type="RESOURCE_LOCAL">
Em persistence.xml:
171. JAVAEE7 JTA (Java Transaction API)
•Disponível em servidores Java EE e suporta transações distribuídas (2-phase)
•Pode ser obtida e injetada como um @Resource em WebServlets, EJBs, e outros
componentes que rodam no container Java EE
•Em ambientes CDI também pode ser injetada com @Inject
public class AlunoDAOBean {
@PersistenceContext(unitName="escola-PU")
private EntityManager;
@Resource UserTransaction ut;
public void addAluno(Aluno aluno) {
try
ut.begin();
em.persist(aluno);
ut.commit();
} catch(Exception e) {
ut.rollback();
} finally { em.close(); }
}
}
<persistence-unit
name="escola-PU"
transaction-type="JTA">
Em persistence.xml:
172. JAVAEE7 CMT (JTA) em EJB
•Em EJB transações gerenciadas pelo container (CMT),JTA é o
comportamento default e é transparente
•Todos os métodos de um SessionBean são automaticamente
incluídos em um contexto transacional
@Stateless
public class AlunoSessionBean {
@PersistenceContext
EntityManager em;
public void addAluno(Aluno aluno) {
em.persist(aluno);
}
}
<persistence-unit
name="escola-PU"
transaction-type="JTA">
Em persistence.xml:
173. JAVAEE7 CMT (JTA) em CDI
•Em CDI o mesmo comportamento pode ser obtido nos métodos
declarados como @Transactional
@Model
public class AlunoSessionBean {
@Inject
EntityManager em;
@Transactional
public void addAluno(Aluno aluno) {
em.persist(aluno);
}
} <persistence-unit
name="escola-PU"
transaction-type="JTA">
Em persistence.xml:
174. JAVAEE7 Cache
•O JPA possui dois níveis de cache
•O Contexto de Persistência, controlado pelo EntityManager (L1)
•Cache L2: Mecanismo compartilhado.
•Cache é um recurso de performance que deve ser usado com cuidado
•Há risco de resultar em inconsistência de dados
•Pode inclusive introduzir bugs
•O custo-benefício com os ganhos de performance poderá compensar o
esforço extra de gerenciar o cache