Nesse post vamos praticar tudo que sabemos sobre o JPA (Java Persistence API) com exercícios de fixação (teóricos e práticos) é uma excelente ferramenta para ajudar você a inserir dados no seu banco. Esse post tem como objetivo principal testar seus conhecimentos sobre essa ferramenta e permitir que você implemente, modifique, melhore, exemplos de código didáticos que utilizam o JPA.
Relembrando…
Se você caiu nesse post e não lembra exatamente o que é o JPA, como ele funciona e pra que ele serve, vou ajudar refrescar sua memória.
Os bancos de dados relacionais são baseados em tabelas (entidades) e relacionamentos, por outro lado, a linguagem Java é Orientada a Objetos. Nesse contexto, podemos perceber que existe uma clara incompatibilidade entre o paradigma orientado à objetos e as tabelas de um banco, portanto, para manipular um banco de dados usando uma linguagem de programação é necessário um esforço adicional do programador.
O JPA faz parte de um conjunto de tecnologias que surgiu dessa necessidade de manipular dados no banco de dados por meio de código e visa facilitar esse trabalho fornecendo ferramentas eficientes para mapear as tabelas em objetos. O termo mapeamento objeto-relacional (ORM) surgiu para denominar ferramentas que fazem esse tipo de “ponte” entre o banco de dados e o código.
No Java existem várias implementações de ORMs como o eclipselink, hibernate, etc. No entanto, cada implementação seguiu suas próprias regras no inicio, causando um problema grave de compatibilidade entre os mapeadores. A solução para esse problema foi a criação de uma API genérica que definia regras mais rigidas de como os softwares ORM deveriam funcionar. Assim, nasceu o JPA – uma interface genérica que auxilia a utilização de ORMs no Java.
Quer relembrar os conceitos? veja um post que fizemos sobre esse assunto:
Questionário
A primeira sequência de exercícios será puramente teórico, nesse questionário temos 10 questões sobre o JPA e detalhes de como utilizar essa ferramenta no Java. Leia com atenção as questões e ao final do questionário disponibilizamos as respostas.
Exercício 1 – Qual é a diferença entre JPA e Hibernate?
- a) Hibernate é especificação e JPA a implementação
- b) Ambos implementações de um ORM
- c) Ambos especificações de um ORM
- d) JPA é especificação e Hibernate a implementação
Exercício 2 – As anotações @Entity, @Id e @GeneratedValue servem, respectivamente, para:
- a) Associar uma classe a uma tabela, indicar a chave primária no banco e permitir que o banco gere automaticamente a chave, de maneira obrigatória.
- b) Associar uma classe a uma tabela, indicar chave primária no banco e permitir o desenvolvedor se encarregue pela geração da chave.
- c) Associar uma classe a uma tabela, indicar chave primária no banco e permitir que o banco gere automaticamente a chave, de maneira opcional.
Exercício 03 – Dado o trecho de código abaixo, diga: Qual é o estado da entidade Aluno neste momento?
Aluno aluno = new Aluno();
aluno.setId(3);
// Conta com Id 3 já foi persistida anteriormente.
aluno.setNome("João Pedro");
aluno.setEndereco("Rua ….");
- a) Managed (Gerenciado)
- b) Transient (Transiente)
- c) New (Novo)
- d) Removed (Removido)
- e) Detached (Desatachado)
Exercício 04 – Veja o código abaixo:
EntityManager manager = new JPAUtil().getEntityManager();
Conta conta = new Conta();
conta.setId(1);
Query query = manager.createQuery("select m from Movimentacao m where m.conta=:pConta" + " and m.tipoMovimentacao=:pTipo");
query.setParameter("pConta", conta);
query.setParameter("pTipo", TipoMovimentacao.ENTRADA);
List<Movimentacao> movimentacoes = query.getResultList();
manager.close()
Assumindo que tudo é compilado e executado corretamente, qual das afirmações abaixo é verdadeira?
- a) A query busca todas as movimentações.
- b) A query busca todas as movimentações de entrada da conta 1.
- c) A query busca todas as movimentações sendo de entrada ou da conta 1.
- d) A query busca a primeira movimentação de entrada.
Exercício 05 – Apesar de termos utilizado anotações para realizarmos o mapeamento entre classes e tabelas, algumas configurações se fazem necessárias no persistence.xml. Uma configuração muito importante é a “unidade de persistência”, qual é a finalidade dessa configuração?
- a) Guardar as mensagens dos erros de validação
- b) Definir a chave primeiro como auto increment
- c) Guardar configurações específicas para um banco de dados
- d) Configurar o mapeamento de relacionamentos
Exercício 06 – Dentro do ciclo de vida de um objeto na JPA, qual estado garante que todas as modificações feitas no objeto serão sincronizadas com o banco de dados?
- a) Removed (Removido)
- b) Transient (Transiente)
- c) New (Novo)
- d) Detached (Desatachado)
- e) Managed (Gerenciado)
Exercício 07 – Observe o relacionamento bidirecional abaixo:
@Entity
public class Aluno {
@OneToOne
private Perfil perfil;
}
@Entity
public class Perfil {
@OneToOne(mappedBy="perfil")
private Aluno aluno;
}
Qual é o lado forte?
- a) O atributo perfil na classe Aluno.
- b) Não há lado forte.
- c) O atributo aluno na classe Perfil.
Exercício 08 – Como se chama o método da interface javax.persistence.Query que devolve um único valor?
- a) getSingleResult()
- b) getUniqueResult()
- c) value()
- d) uniqueValue()
- e) getOneResult()
Exercício 09 – Qual das afirmações abaixo NÃO é verdadeira?
- a) Relacionamentos LAZY, ao ser inicializados, precisam do EntityManager aberto.
- b) Relacionamentos para-muitos (*ToMany) são LAZY por padrão.
- c) Relacionamentos LAZY não podem ser carregados antecipadamente, nem pelo JPQL.
- d) Relacionamentos LAZY serão inicializados sob demanda (ao acessar).
Exercício 10 – O método find() e o getReference() da interface EntityManager permitem que:
- a) Encontre a relação entre o id passado e os objetos Transient da sessão com o banco.
- b) Recuperemos o endereço de memória de um objeto através do seu id.
- c) Recuperemos uma instância de uma entidade no banco de dados através do seu id.
- d) Busquemos entre os objetos em memória, quais já foram persistidos.
Quer ver um exemplo sobre como implementar um aplicação usando JPA? Veja esse exemplo no github.
Respostas do questionário
- 1 – d
- 2 – c
- 3 – e
- 4 – b
- 5 – c
- 6 – e
- 7 – a
- 8 – a
- 9 – c
- 10 – c
Exercícios práticos
Agora que você já passou pelos posts teóricos vamos colocar a mão na massa com alguns exercícios práticos sobre JPA para treinar seus conhecimentos. Esses exercícios de JPA não possuem alta complexidade e têm a finalidade exclusivamente educacional, as resoluções estão presentes também no nosso github.
A programação java é um assunto bastante extenso, se você quer saber mais e ver mais exercícios clique aqui e acesse os exercícios.
Exercicio 1 – Construindo uma aplicação com JPA
Agora que já fizemos vários exercícios teóricos sobre JPA, vamos continuar nos aprofundando no assunto, mas agora vamos colocar a mão no código e tentar implementar algo um pouco diferente.
Veja as regras do exercício:
- Neste exercício você deverá ler todo o enunciado e ao final entregar o código Java que supre todas os requisitos descrito.
- Você pode utilizar IDEs de programação (NetBeans, eclipse, etc…), no entanto, é fortemente recomendado que você compreenda TODOS os códigos gerados pela IDE. O risco de se acomodar é seu.
- Recomendamos que você utilize no mínimo 2 bases de dados diferentes para testar o funcionamento do JPA (MySQL e Postgres são as mais comuns).
Compreendendo a lógica que deve ser implementada.
Neste exercício você irá criar um conjunto de entidades com um relacionamento muito simples. As entidades serão: Pessoa (contendo: id, login, senha, email, nome); Compra (contendo: id, valor, data, observacao).
Essa lógica representa o relacionamento que um cliente tem em qualquer loja onde ele pode realizar uma compra. Um cliente pode ter várias compras, no entanto, uma compra pode pertencer a apenas um cliente.
Quais bibliotecas você pode (deve) utilizar
Para completar este exercício você deverá utilizar o JPA 2.x como especificação. Como implementação do JPA você pode escolher entre o Hibernate e o eclipselink. As bases de dados ficam a sua escolha, porém recomendamos o Postgres e o MySQL.
Sugestão de passos para completar o exercício
- Passo 1 – Faça a instalação das IDE’s, Banco de dados e configure o Java.
- Passo 2 – Crie um projeto em sua IDE de preferência
- Passo 3 – Adicione as bibliotecas necessárias ( JPA, Hibernate ou eclipselink, Driver do banco de dados)
- Passo 4 – Crie as classes de entidade
@Entity
@Table(name = "compra")
public class Compra implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "id")
private Integer id;
@Column(name = "valor")
private String valor;
@Column(name = "data")
@Temporal(TemporalType.DATE)
private Date data;
@Column(name = "observacao")
private String observacao;
@Column(name = "recebido")
private Boolean recebido;
{{getters and setters}}
}
- Passo 5 – Crie os controladores contendo os métodos:
public class CompraJpaController{
public void create(){}
public void edit(){}
public void destroy(){}
public void findAll(){}
public void findById(){}
}
public class EmProvider {
private static final String DB_PU = "AplicacaoAulaPU";
public static final boolean DEBUG = true;
private static final EmProvider singleton = new EmProvider();
private EntityManagerFactory emf;
private EmProvider() {}
public static EmProvider getInstance() {
return singleton;
}
public EntityManagerFactory getEntityManagerFactory() {
if(emf == null) {
emf = Persistence.createEntityManagerFactory(DB_PU);
}
if(DEBUG) {
System.out.println("factory created on: " + new Date());
}
return emf;
}
public void closeEmf() {
if(emf.isOpen() || emf != null) {
emf.close();
}
emf = null;
if(DEBUG) {
System.out.println("EMF closed at: " + new Date());
}
}
}
- Passo 6 – Crie uma classe de teste contendo um simples método Main();
- Passo 7 – Dentro da classe Main(), faça:
public class TestandoPersistencia {
public static void main(String[] args) {
Pessoa p = new Pessoa();
Date d = new Date();
p.setNome("Isabela");
p.setLoggin("isabela tal");
p.setEmail("[email protected]");
p.setSenha("teste");
List<Compra> compras = new ArrayList<>();
Compra c1 = new Compra();
c1.setData(d);
c1.setObservacao("compra impressora");
c1.setRecebido(true);
c1.setValor("890");
CompraJpaController c =
new CompraJpaController(EmProvider.getInstance().getEntityManagerFactory());
c.create(c1);
compras.add(c1);
p.setCompraList(compras);
PessoaJpaController j = new PessoaJpaController(EmProvider.getInstance().getEntityManagerFactory());
j.create(p);
System.out.println(j.findPessoaEntities());
}
}
- Passo 8 – Teste!
Faça download do exercício resolvido no github:
Exercicio 2 – Configurando o Fetch Type do JPA
No primeiro exercício de JPA um dos grandes problemas a serem resolvidos é a questão da eficiência ao realizar as buscas no banco de dados. É muito comum que ao programar utilizando esta API o programador tenha q se preocupar com cada comando executado, pois estes comandos resultam em operações que podem se tornar custosas em uma aplicação que recebe muitas requisições.
O JPA possui uma opção muito utilizada que é o FechType. Essa opção controla quais entidades serão carregadas ao realizar uma operação de SELECT na base de dados. Considere o seguinte diagrama de entidade-relacionamento:
Neste caso você pode observar que existem duas entidades e uma possui uma relação OneToMany com a outra (pessoa-compra).
Pense um pouco…
Ao realizar uma busca por compras, como o resultado é expressado?
SELECT * FROM compra;
output:
Se você conhece um pouco de SQL sabe que a coluna “pessoa_idpessoa” é a coluna que realiza a conexão entre as tabelas. Os números ali inseridos são as chaves primárias da tabela “pessoa”.
A partir deste ponto, toda a teoria dos InnerJoins, selects aninhados são aplicadas para gerar resultados interessantes para a aplicação e mostrar ao usuário.
Onde o JPA ajuda?
Utilizando o JPA é possível buscar todas as pessoas e dentro delas acessar uma lista de compras feitas por elas. O problema de fazer isso é que muitas vezes não queremos que esses dados sejam carregados, assim precisamos controlar se esses relacionamentos serão mesmo carregados ao realizar um select. Para que isso seja realizado podemos utilizar o FechType.
O funcionamento do FetchType
Para resolver esses exercícios de JPA precisamos compreender que existem 2 tipos de “fetch”: LAZY loading, EAGER loading. No LAZY loading NÃO são carregados os relacionamentos dentro da busca principal. Já no EAGER loading, tudo será carregado.
Veja como utilizar o Lazy Loading:
import javax.persistence.FetchType;
@OneToOne(fetch=FetchType.LAZY)
@JoinColumn(name="user_profile_id")
private Profile getUserProfile() {
return userProfile;
}
Para utilizar o Eager Loading basta substituir a linha para:
@OneToOne(fetch=FetchType.EAGER)
Exemplo de output
Ao realizar uma operação de busca utilizando o EAGER loading , por exemplo:
controlPessoa.findEntities()
O resultado será algo como:
Pessoa{idPessoa=1
, nome=vinicius dos santos
, login=vinicius
, [email protected]
, senha=1234
, compraList=[model.Compra[ idCompra=1 ], model.Compra[ idCompra=4 ]]}
Pessoa{idPessoa=2
, nome=Mario da silva
, login=mario
, [email protected]
, senha=1234
, compraList=[model.Compra[ idCompra=3 ]]}
Pessoa{idPessoa=3
, nome=Gabriel garcia
, login=gabriel
, [email protected]
, senha=1234
, compraList=[model.Compra[ idCompra=2 ]]}
Pessoa{idPessoa=4
, nome=Isabela Ribeiro
, login=isabela
, [email protected]
, senha=1234
, compraList=[]}
Ao realizar a mesma busca utilizando o LAZY loading, o resultado será:
Pessoa{idPessoa=1
, nome=vinicius dos santos
, login=vinicius
, [email protected]
, senha=1234
, compraList={IndirectList: not instantiated}}
Pessoa{idPessoa=2
, nome=Mario da silva
, login=mario
, [email protected]
, senha=1234
, compraList={IndirectList: not instantiated}}
Pessoa{idPessoa=3
, nome=Gabriel garcia
, login=gabriel
, [email protected]
, senha=1234
, compraList={IndirectList: not instantiated}}
Pessoa{idPessoa=4
, nome=Isabela Ribeiro
, login=isabela
, [email protected]
, senha=1234
, compraList={IndirectList: not instantiated}}
O resultado de indirect list not instantiated representa a lista não carregada devido ao lazy loading. Como exercício você deverá implementar as classes definidas na Figura 1 e testar a utilização do LAZY e do EAGER loading.
Acesse a resposta aqui:
E ai, gostou dos exercícios JPA? Tem sugestões? deixa nos comentários 🙂