JPA DAO Genérico e JPQL Paulo Ricardo Lisboa de Almeida 1
Criando um DAO JPA public abstract class GenericDAO<ID,U>{... Onde T definirá a chave dos objetos, e U definirá os objetos em si (da mesma forma que no IGeneriDAO criado para o JDBC) 2
Atributos do Generic DAO Atributos O que significa o EntitymanagerFactory ser static final? private static final EntityManagerFactory emf = Persistence.createEntityManagerFactory("clinica_pu"); private EntityManager em; private Class<U> entityclass; 3
Atributos do Generic DAO Atributos O que significa o EntitymanagerFactory ser static final? Temos somente um factory para a aplicação inteira Instanciar o factory é extremamente custoso. Portanto ele deve ser criado somente uma vez private static final EntityManagerFactory emf = Persistence.createEntityManagerFactory("clinica_pu"); private EntityManager em; private Class<U> entityclass; 4
O Construtor public GenericDAO(Class<U> entityclass) { this.entityclass = entityclass; 5
Métodos Básicos da Classe public void begintransaction() { em = emf.createentitymanager(); em.gettransaction().begin(); public void commit() { em.gettransaction().commit(); public void rollback() { em.gettransaction().rollback(); public void rollbackandclosetransaction() { try{ this.em.gettransaction().rollback(); finally{ this.closetransaction(); 6
Métodos Básicos da Classe private void closetransaction() { this.em.close(); public void commitandclosetransaction() { try{ this.commit(); finally{ this.closetransaction(); public void flush() { em.flush(); 7
Métodos Para Manipulação de Objetos public void save(u entity) { em.persist(entity); public void delete(u entity) { em.refresh(entity); em.remove(entity); public U update(u entity) { return em.merge(entity); public U find(id entityid) { return em.find(entityclass, entityid); public U findreferenceonly(id entityid) { try{ return em.getreference(entityclass, entityid); catch(entitynotfoundexception e){ return null; 8
Quando e como fechamos o EntityManagerFactory? Pode-se capturar um evento de fechamento da aplicação e fechar o factory Pode-se também utilizar APIs que gerenciam o factory automaticamente, criar um FactoryUtils, etc. No exemplo, o factory deverá ser fechado manualmente através do método do GenericDAO: public static void fecharentitymanagerfactory(){ emf.close(); 9
Estendendo o DAO public class PessoaDAO extends GenericDAO<Integer, Pessoa>{ public PessoaDAO() { super(pessoa.class); 10
Utilizando o DAO public static void main(string[] args) { PessoaDAO pessoadao = new PessoaDAO(); try{ pessoadao.begintransaction(); Pessoa p = pessoadao.find(6); System.out.println(p.getNome()); for(animal a : p.getanimais()) System.out.println(a.getNome()); pessoadao.commitandclosetransaction(); System.out.println("Fim!!!"); catch(exception e){ pessoadao.rollbackandclosetransaction(); e.printstacktrace(); finally{ GenericDAO.fecharEntityManagerFactory(); System.out.println("Saindo!!!"); 11
JPQL Java Persistence Query Language Forma de fazer consultas orientadas a objetos Independente de banco de dados Flexibilidade nas consultas 12
JPQL - EXEMPLO public class PessoaDAO extends GenericDAO<Integer, Pessoa>{ public List<Pessoa> listar(){ String sql = "select p from Pessoa p"; Query query = super.getentitymanager().createquery(sql);... @SuppressWarnings("unchecked") List<Pessoa> retorno = query.getresultlist(); return retorno; Repare que no comando select Pessoa se refere a classe Pessoa (inclusive o P de Pessoa necessariamente deve ser maiúsculo, como o nome na classe) e não a tabela pessoa do banco de dados. 13
@SuppressWarnings("unchecked") Type safety: The expression of type List needs unchecked conversion to conform to List<Pessoa> PessoaDAO.java Não é possível verificar em tempo de compilação o tipo T da lista retornada por getresultlist Type Erasure https://docs.oracle.com/javase/tutorial/java/generics/erasu re.html O Generic em Java não é covariante http://docs.oracle.com/javase/tutorial/extra/generics/subty pe.html @SuppressWarnings("unchecked") List<Pessoa> retorno = query.getresultlist(); 14
JPQL Queries mais complexas Exemplo de método para retornar uma lista de pessoas que possuem animais de um tipo específico: public List<Pessoa> listarpessoasquepossuemtipoanimal(integer idtipoanimal){ String sql = "select p from Pessoa p" + " inner join p.animais a" + " where a.tipoanimal.id = :idtipoanimal" + " group by p"; Query query = super.getentitymanager().createquery(sql); query.setparameter("idtipoanimal", idtipoanimal); @SuppressWarnings("unchecked") List<Pessoa> retorno = query.getresultlist(); return retorno; 15
JPQL Queries mais Complexas Note o bind entre o parâmetro : idtipoanimal e o identificador do tipoanimal especificado pelo query.setparameter("idtipoanimal", idtipoanimal); 16
Join Fetch Caso seja necessário criar uma consulta JPQL que busque uma pessoa e sua lista de animais de maneira eager, devese utilizar um join fetch. Ex.: public Pessoa findeager(integer id){ String sql = "select p from Pessoa p " + " inner join fetch p.animais" + " where p.id = :idpessoa"; TypedQuery<Pessoa> query = super.getentitymanager().createquery(sql, Pessoa.class); query.setparameter("idpessoa", id); try { Pessoa retorno = query.getsingleresult(); return retorno; catch (NoResultException ex) { return null; 17
TypedQuery TypedQuery é o mesmo que Query, porém o TypedQuery força o tipo retornado (no caso para pessoa), diminuindo a probabilidade de erros de programação Alguns erros podem ser pegos em tempo de compilação. 18
JPQL - Performance Ao criar uma consulta dentro de um método, como no exemplo, o código JPQL precisa ser interpretado e transformado em um código sql válido para o banco. 19
Named Query São comandos JPQL pré-processados no momento da inicialização da unidade de persistência Anotações nas classes das entidades Uma named query deve ter um nome único, portanto é uma boa prática colocar NOME_ENTIDADE.NOME_QUERY para que isso seja garantido @Entity @Table(name="pessoa") @NamedQuery (name="pessoa.findbycpf", query = " SELECT p FROM Pessoa p where p.cpf = :cpf" ) public class Pessoa {... 20
Utilizando Uma Named Query Dentro do DAO public class PessoaDAO extends GenericDAO<Integer, Pessoa>{ public Pessoa findbycpf(integer cpf){ Query query = super.getentitymanager().createnamedquery("pessoa.findbycpf"); query.setparameter("cpf", cpf);... Pessoa retorno = (Pessoa)query.getSingleResult(); return retorno; 21
Single Result query.getsingleresult() especifica que somente um item será retornado. Se não existir o item no banco, será lançada uma exceção NoResultException Retornando null caso o item não exista no banco: public Pessoa findbycpf(integer cpf){ Query query = super.getentitymanager().createnamedquery("pessoa.findbycpf"); query.setparameter("cpf", cpf); try { Pessoa retorno = (Pessoa)query.getSingleResult(); return retorno; catch (NoResultException ex) { return null; 22
Múltiplas Named QUeries Utiliza-se a anotação @NamedQueries @NamedQueries({ @NamedQuery (name="pessoa.findbycpf", query = " SELECT p FROM Pessoa p where p.cpf = :cpf" ), @NamedQuery(name="Pessoa.listSemDataNascimento", query="select p FROM Pessoa p WHERE c.nascimento is null"), ) 23
Problemas com Named Queries? public List<Pessoa> listar(boolean ordenacaoascendente){ String strorderby; if(ordenacaoascendente == true) strorderby = " order by p.nome asc"; else strorderby = " order by p.nome desc"; String sql = "select p from Pessoa p" + strorderby; Query query = super.getentitymanager().createquery(sql); @SuppressWarnings("unchecked") List<Pessoa> retorno = query.getresultlist(); return retorno; 24
Problemas com Named Queries? As consultas devem ser pré-definidas A seguinte query não pode se tornar uma named query, pois o comando é construído dinamicamente. public List<Pessoa> listar(boolean ordenacaoascendente){ String strorderby; if(ordenacaoascendente == true) strorderby = " order by p.nome asc"; else strorderby = " order by p.nome desc"; String sql = "select p from Pessoa p" + strorderby; Query query = super.getentitymanager().createquery(sql); @SuppressWarnings("unchecked") List<Pessoa> retorno = query.getresultlist(); return retorno; 25
Fim! Resolva os exercícios propostos 26