Aula JPA
-
Upload
marco-reis -
Category
Technology
-
view
524 -
download
4
description
Transcript of Aula JPA
Java Persistence APIJPA
Marco ReisSoftware Architecthttp://marcoreis.net
Agenda• Mapeamento objeto-relacional.
• JPA x Hibernate.
• Criação das tabelas.
• Executar comandos de inclusão, alteração, exclusão e consulta.
Código-fonte• O projeto de exemplo com o código-fonte está disponível
em:
– https://github.com/masreis/e-commerce
Mapeamento objeto-relacional• JPA é um modelo de persistência baseado
em POJO.• Java é utilizada em ambientes corporativos,
com grandes bancos de dados.• Agiliza o desenvolvimento, já que não
precisamos escrever comandos SQL.• Fácil de mudar de banco de dados, caso
necessário.
JPA x Hibernate
• JPA é a especificação da Oracle para mapenamento objeto-relacional.
• Hibernate apresentava uma maneira mais elegante de trabalhar com a persistência do que o J2EE.
Arquivo de configuração• O arquivo persistence.xml contém os parâmetros de
configuração para acesso ao banco de dados e deve estar no diretório META-INF.
• Persistence unit name (e-commerce-pu): indica os parâmetros de acesso à base de dados.
• transaction-type: RESOURCE_LOCAL para servidor web e JTA para servidor de aplicação.
• Provider: qualquer implementação do JPA. Pode ser o Hibernate, OpenJPA ou EclipseLink, que é o produto oficial da Oracle.
persistence.xml<?xml version="1.0" encoding="UTF-8"?><persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0" xmlns="http://java.sun.com/xml/ns/persistence">
<persistence-unit name="e-commerce-pu" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect" /> <property name="hibernate.hbm2ddl.auto" value="update" /> <property name="hibernate.show_sql" value="true" /> <property name="hibernate.format_sql" value="true" />
<property name="javax.persistence.jdbc.driver" value="org.hsqldb.jdbcDriver" /> <property name="javax.persistence.jdbc.url" value="jdbc:hsqldb:file:e-commerce/db;shutdown=true;hsqldb.write_delay=false;" /> <property name="javax.persistence.jdbc.user" value="SA" /> <property name="javax.persistence.jdbc.password" value="" /> </properties> </persistence-unit></persistence>
Entidades• Com JPA podemos mapear as classes de entidade
diretamente para o banco de dados.
• Vamos considerar as seguintes classes:
– Usuario
– Cliente
– Produto
– Categoria
• As anotações do JPA estão no pacote javax.persistence.
Classe Usuariopackage net.marcoreis.ecommerce.entidades;
import java.util.Date;
public class Usuario { private Long id; private String email; private String nome; private Date ultimoLogin;
{sets e gets}
}
Classe Usuario persistente@Entitypublic class Usuario { @Id @GeneratedValue private Long id; private String email; private String nome; private Date ultimoLogin;}
• Para persistir uma classe usando JPA é necessário adicionar ao menos as seguintes anotações:
– @Entity: a classe será armazenada em uma tabela.
– @Id: campo da chave-primária.
– @GeneratedValue: a chave-primária será gerada automaticamente.
Teste unitário com JUnit
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version></dependency>
• As funcionalidades serão testadas com o Junit.
• Cada funcionalidade será validada com um teste unitário.
• As classes de teste vão ficar no pacote:
– net.marcoreis.ecommerce.teste
• As classes do JUnit estão em org.junit.
• Para rodar um teste, vá no menu Run-Run as-JUnit test.
• Adicione a dependência do Maven no pom.xml.
• Utilize o @Before e @After para inicializar e finalizar variáveis. Quando o teste envolver inclusão, alteração ou exclusão de registros, é obrigatório o begin() e commit().
Inserindo registrospublic class TesteInsereUsuario {
private EntityManager em;
@Before public void inicializar() { em = JPAUtil.getInstance().getEntityManager(); em.getTransaction().begin(); }
@After public void finalizar() { em.getTransaction().commit(); em.close(); }
@Test public void inserirUsuario() { Usuario usuario = new Usuario(); usuario.setEmail("[email protected]"); usuario.setNome("Marco Reis"); Date data = new Date(); usuario.setUltimoLogin(data); em.persist(usuario); }}
Consulta registros gravados
@Entity@NamedQuery(name = "usuario.consultaAcessoDia", query = "from Usuario where cast(ultimoLogin as date) = :data")public class Usuario { @Id @GeneratedValue private Long id; private String email; private String nome; private Date ultimoLogin;}
• Para consultar os registros já gravados vamos usar uma Query ou uma NamedQuery.
• A NamedQuery fica na classe persistente, enquanto que a Query pode estar em qualquer outra classe do sistema.
• Os parâmetros podem usar as seguintes formas:– :nomeParametro1, :nomeParametro2.
– ?1, ?2, ?3.
NamedQuery
@Entity@NamedQuery(name = "usuario.consultaAcessoDia", query = "from Usuario where cast(ultimoLogin as date) = :data")public class Usuario { @Id @GeneratedValue private Long id; private String email; private String nome; private Date ultimoLogin;}
• Adicione uma NamedQuery em Usuario.
• O cast é necessário porque a data é armazenada em timestamp, e a consulta deve considerar somente a data, ignorando a hora/minuto/segundo.
Consulta registros gravados
public class TesteConsultaUsuarios {
private EntityManager em;
@Before public void inicializar() { em = JPAUtil.getInstance().getEntityManager(); }
@After public void finalizar() { em.close(); }
}
• Em seguida, vamos criar o teste unitário para verificar os usuários cadastrados.
Consulta registros gravados
@Test public void consultaTodosUsuarios() { String queryJPA = "from Usuario"; Query query = em.createQuery(queryJPA); List<Usuario> usuarios = query.getResultList(); for (Usuario usuario : usuarios) { System.out.println("Nome: " + usuario.getNome()); } }
• O primeiro teste unitário utiliza uma Query, retornando todos os usuários cadastrados.
Consulta registros gravados
@Test public void consultaUsuriosAcessoDia() { em = JPAUtil.getInstance().getEntityManager(); Query query = em.createNamedQuery("usuario.consultaAcessoDia"); query.setParameter("data", new Date()); List<Usuario> usuarios = query.getResultList(); for (Usuario usuario : usuarios) { System.out.println("Nome/ultimo login: " + usuario.getNome() + " - " + usuario.getUltimoLogin()); } }
• O teste unitário abaixo acessa a NamedQuery e passa um parâmetro, mostrando apenas os registros que têm a data de hoje.
Alterando registros gravados• Para os testes abaixo crie a classe TesteAlteraUsuario.
• Para alterar um registro, primeiro faça uma consulta pelo ID. Dessa forma, o objeto será gerenciado pelo EntityManager e poderá ser atualizado.
@Test public void alterarUsuario() { Long id = 2l; Usuario usuario = em.find(Usuario.class, id); Assert.assertNotNull("Usuario não cadastrado", usuario); usuario.setEmail("[email protected]"); usuario.setNome("Diego Lucas"); Date data = new Date(); usuario.setUltimoLogin(data); em.persist(usuario); }
Removendo registros gravados• Para os testes abaixo crie a classe
TesteRemoveUsuario.
• A exclusão deve ser feita após uma consulta pelo ID, seguindo a mesma ideia da alteração.
@Test public void removerUsuario() { Long id = 5l; Usuario usuario = em.find(Usuario.class, id); Assert.assertNotNull("Usuario não cadastrado", usuario); em.remove(usuario); }
Herança• Há 3 estratégias de herança no JPA:
– JOINED: uma tabela para cada entidade, não repete os campos.
– SINGLE_TABLE: apenas uma tabela para todas as classes.
– TABLE_PER_CLASS: uma tabela para cada classe, repetindo todos os campos.
Classe Cliente• Para mostrar o funcionamento da herança no JPA, crie a
classe Cliente, subclasse de Usuario.
• Não é necessário redefinir o Id, que já está na superclasse.@Entitypublic class Cliente extends Usuario { @Column(unique = true, nullable = false) private String cpfCnpj;
{sets e gets}}
Teste do Cliente• Agora, crie a classe TesteInsereCliente para testar a
nova entidade.
• Não se esqueça dos métodos de inicialização e finalização.
• A título de teste, comente a linha do cpfCnpj e veja o resultado.
@Test public void inserirCliente() { Cliente cliente = new Cliente(); cliente.setEmail("[email protected]"); cliente.setNome("Jose Carlos"); // cliente.setCpfCnpj("123456"); em.persist(cliente); Assert.assertTrue("Cliente gravado com sucesso", cliente.getId() > 0); }
Como funciona• Acompanhando o log do Hibernate podemos verificar
que foram incluídos 2 registros, um na tabela Usuario e outro na tabela Cliente.
• Isso porque a estratégia de herança selecionada foi JOINED.Hibernate: insert into Usuario (id, email, nome, ultimoLogin) values (default, ?, ?, ?)Hibernate: insert into Cliente (cpfCnpj, id) values (?, ?)
Atividade• Crie as classes de teste Categoria:
– Inclusão.
– Alteração.
– Exclusão.
– Consulta todas as categorias.
Relacionamentos• O JPA tem as seguintes multiplicidades, seguindo o
modelo relacional:
– many-to-one.
– one-to-one.
– one-to-many.
– many-to-many.
Many-to-one• O relacionamento many-to-one está presente na classe
produto.
• Segundo o modelo, cada produto deve ter uma categoria.public class Produto { @Id @GeneratedValue private Long id; @ManyToOne private Categoria categoria; private String nome; private String descricao; private String especificacaoLoja; @Lob private byte[] especificacaoFabricante; private Double preco;}
Inserindo produtos• Para inserir um produto precisamos escolher uma
categoria válida.
• O primeiro passo é pesquisar uma categoria já cadastrada.
• Em seguida podemos preencher os demais atributos e persistir a entidade.
@Test public void inserirProduto() { Long idCategoria = 12l; Categoria categoria = em.find(Categoria.class, idCategoria); Assert.assertNotNull("Categoria não cadastrada", categoria); Produto produto = new Produto(); produto.setCategoria(categoria); produto.setDescricao("Colcha para cama de solteiro 120cm x 210cm"); produto.setNome("Colcha para cama de solteiro"); produto.setPreco(150.00); em.persist(produto); }
Consultando produtos• Para os exemplos seguintes crie a classe
TesteConsultaProdutos.
• O primeiro teste, consultarTodasCategorias, segue o mesmo princípio das demais.
@Test public void consultarTodosProdutos() { System.out.println("Consultar todos os produtos"); List<Produto> produtos = em.createQuery("from Produto").getResultList(); for (Produto p : produtos) { System.out.println(p.getId() + " - " + p.getNome()); } }
Consultando produtos pela categoria• O exemplo abaixo mostra os produtos de uma categoria
específica.
• Este teste usa uma Query, mas poderia ser facilmente adaptado para utilizar NamedQuery.
@Test public void consultarProdutosPelaCategoria() { System.out.println("Consultar produtos pela categoria"); Long idCategoria = 12l; List<Produto> produtos = em .createQuery("from Produto where categoria.id = ?1") .setParameter(1, idCategoria ).getResultList(); for (Produto p : produtos) { System.out.println(p.getId() + " - " + p.getNome()); } }
Consultando quantidades• Adicione as NamedQueries abaixo em Produto e o teste
na classe TesteConsultaProdutos.
@Test public void consultarTotalProdutoPelaCategoria() { System.out.println("Consultar total de produtos pela categoria"); Query query = em.createNamedQuery("produto.consultaTotalPorCategoria"); Long idCategoria = 12l; query.setParameter("idCategoria", idCategoria); Object resultado = query.getSingleResult(); System.out.println("Total de produtos na categoria: " + resultado); }
@NamedQueries({ @NamedQuery(name = "produto.consultaTotalPorCategoria", query = "select count(p) from Produto p where categoria.id = :idCategoria"), @NamedQuery(name = "produto.consultaPorIntervaloPreco", query = "from Produto where preco >= ?1 and preco <= ?2") })
Atividades• Criar o teste unitário para a NamedQuery
'produto.consultaPorIntervaloPreco' na classe TesteConsultaProdutos.
• Criar as classes abaixo, com seus relacionamentos, atributos e testes unitários.
– Venda (id, data, cliente).
– Item (id, produto, quantidade, valorUnitario, venda).
Referência• http://docs.oracle.com/javaee/6/tutorial/