Introdução ao JPA
Prof. Bruno Ferreira
Mapeamento Objeto Relacional
(ORM)
Mapeamento Objeto Relacional
(ORM)
Mapeamento Objeto Relacional
(ORM)
Mapeamento Objeto Relacional
(ORM)
Mapeamento Objeto Relacional
(ORM)
Download do Hibernate
Instalando o Hibernate
1) Descompacte os arquivo baixado
2) Dentro do Eclipse crie uma variável de usuário na opção de preferências. 3) Adicione todos os arquivos .jar dos diretórios “required” e “jpa”
Download do JDBC-MySql
Instalando o JDBC-MySql
1) Descompacte os arquivo baixado
2) Dentro do Eclipse defina um novo na opção de preferências. 3) Adicione o arquivo .jar referente ao driver
Salvando o primeiro objeto no Banco
de Dados
CREATE TABLE cliente (
codigo BIGINT NOT NULL AUTO_INCREMENT, nome VARCHAR(100) NOT NULL,
idade INTEGER,
sexo VARCHAR(1) NOT NULL, profissao VARCHAR(30),
Nome do BD: cadastro01
profissao VARCHAR(30), PRIMARY KEY (codigo) );
CREATE TABLE conta_corrente (
codigo BIGINT NOT NULL AUTO_INCREMENT, numero VARCHAR(12) NOT NULL,
saldo DECIMAL,
codigo_cliente BIGINT NOT NULL, PRIMARY KEY (codigo),
FOREIGN KEY (codigo_cliente) REFERENCES cliente (codigo) );
Salvando o primeiro objeto no Banco
de Dados
1) Crie um novo projeto java e informe o nome do projeto. 2) Na aba Libraries, clique no botão “Add Library...”
Salvando o primeiro objeto no Banco
de Dados
1) Ainda na aba Libraries, clique no botão “Add Library...” 2) Escolha o item “User Library” e clique em Next
Salvando o primeiro objeto no Banco
de Dados
1) Crie uma estrutura de diretórios similar a figura abaixo
2) Na pasta META-INF crie o arquivo de configuração do Hibernate “persistence.xml” 3) Configure o arquivo xml como descrito abaixo.
Salvando o primeiro objeto no Banco
de Dados
Outro exemplo de persistence.xml:
....
<persistence-unitname="financeiroPU" transaction-type="RESOURCE_LOCAL"> <properties>
<propertyname="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/financeiro" /> <propertyname="javax.persistence.jdbc.user" value="root" />
<propertyname="javax.persistence.jdbc.password" value="root" />
<propertyname="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
Estrutura de diretórios no Netbeans
<propertyname="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
<propertyname="javax.persistence.schema-generation.database.action" value="drop-and-create" /> <propertyname="javax.persistence.schema-generation.create-source" value="metadata" />
<propertyname="javax.persistence.schema-generation.drop-source" value="metadata" />
<propertyname="javax.persistence.sql-load-script-source" value="META-INF/sql/carregar-dados.sql"/> <propertyname="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
<propertyname="hibernate.show_sql" value="true" /> <propertyname="hibernate.format_sql" value="true" /> </properties>
</persistence-unit> ....
Salvando o primeiro objeto no Banco
de Dados
1) Crie a classe Cliente com as propriedades necessários e gere os métodos gets and sets; 2) Faça as anotações conforme mostra a imagem.
import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id;
public class Cliente implements Serializable { @Id
@GeneratedValue(strategy=GenerationType.IDENTITY) private Long codigo;
private String nome; private Integer idade; private String sexo;
private String profissao; gets e sets....
construtor sem parâmetro... }
Salvando o primeiro objeto no Banco
de Dados
1) Crie a classe Principal do sistema com o seguinte método main public class Principal {
public static void main(String[] args) { EntityManagerFactory emf =
Persistence.createEntityManagerFactory("cadastro01PU"); EntityManager em = emf.createEntityManager();
EntityManager em = emf.createEntityManager(); Cliente cliente = new Cliente();
cliente.setNome("Tião Jeferson"); cliente.setIdade(20); cliente.setProfissao("Estudante"); cliente.setSexo("M"); em.getTransaction().begin(); em.persist(cliente); em.getTransaction().commit(); }
Consultando o primeiro objeto no
Banco de Dados
1) Insira o código abaixo no final do método main public class Principal {
public static void main(String[] args) { ....
//---Cliente retorno = em.find(//---Cliente.class, 3L); Cliente retorno = em.find(Cliente.class, 3L);
if (retorno != null) {
System.out.println("Nome: " + retorno.getNome()); System.out.println("Idade: " + retorno.getIdade()); } else {
System.out.println("Cliente não encontrado."); }
Removendo o primeiro objeto no
Banco de Dados
1) Insira o código abaixo no final do método main public class Principal {
public static void main(String[] args) { ....
//---Cliente cliente = em.find(//---Cliente.class, 2L);
Funciona??
Cliente cliente = Cliente(); cliente.setCodigo(2);
Erro: Not detached
Cliente cliente = em.find(Cliente.class, 2L); em.getTransaction().begin();
em.remove(cliente);
em.getTransaction().commit();
System.out.println("Objeto removido com sucesso!"); }
Atualizando o primeiro objeto no
Banco de Dados
1) Insira o código abaixo no final do método main public class Principal {
public static void main(String[] args) { ....
//---Cliente retorno = em.find(//---Cliente.class, 3L); Cliente retorno = em.find(Cliente.class, 3L);
if (retorno != null) {
em.getTransaction().begin();
retorno.setNome(“Tião da Silva”); em.getTransaction().commit(); } else {
System.out.println("Cliente não encontrado."); }
Exercício
1) Fazendo seu primeiro CRUD
a) Crie um modelo como o mostrado na classe abaixo.
Dica: Para o mapeamento utilize @Temporal(TemporalType.TIMESTAMP) para o atributo do tipo data (java.util.Date).
b) Crie uma classe chamada CrudAgenda que deve ter os seguintes métodos: Salvar, Consultar, Atualizar e Remover.
Entendendo um pouco mais da
transação
Double valorTransferencia = entrada.nextDouble(); Double valorTransferencia = entrada.nextDouble(); em.getTransaction().begin();
conta1.setSaldo(conta1.getSaldo() - valorTransferencia); conta2.setSaldo(conta2.getSaldo() + valorTransferencia); if (conta1.getSaldo() > 0) {
em.getTransaction().commit();
System.out.println("Transferência realizada com sucesso!"); }
else {
em.getTransaction().rollback();
System.err.println("Transferência não realizada, saldo insuficiente!"); }
A Java Persistence Query Language
(JPQL)
Primeiro contato com o JPQL
1) Insira o código abaixo no final do método main public class Principal {
public static void main(String[] args) { ....
//---List<Cliente> clientes =
List<Cliente> clientes =
em.createQuery("from Cliente where sexo = 'M'", Cliente.class) .getResultList(); for (Cliente cliente : clientes) {
System.out.println("Código: " + cliente.getCodigo()); System.out.println("Nome: " + cliente.getNome()); System.out.println("Sexo: " + cliente.getSexo()); System.out.println("---"); } }
Fechando o Entity Manager
1) Quanto você fecha o EntityManager todos os objetos não são mais gerenciados. public class Principal {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("exemploPU"); EntityManager em = emf.createEntityManager();
Cliente cliente = new Cliente(); cliente.setNome(“Tião"); em.getTransaction().begin(); em.persist(cliente); em.getTransaction().commit(); em.close(); em = emf.createEntityManager(); em.merge(cliente);
//<agora você pode trabalhar com o cliente pois ele volta a ser gerenciado>
Cadastrando o primeiro objeto
Fabricante - Modelo
import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity @Entitypublic class Fabricante {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name=“id”)
private Long codigo; private String nome; //gerar os gets e o sets }
Fabricante - Modelo
Implemente os métodos equals() e hashcode()!
Fabricante - DAO
public class FabricanteDAO implements Serializable { private EntityManager em;
public void salvar(Fabricante fabricante) { //não esqueça de criar o em!
em.getTransaction().begin(); em.persist(fabricante);
em.persist(fabricante);
em.getTransaction().commit(); } public List<Fabricante> buscarTodos() {
return em.createQuery("from Fabricante").getResultList(); } public void excluir(Fabricante fabricante) throws NegocioException {
Fabricante fabricanteTemp = em.find(Fabricante.class, fabricante.getCodigo()); //inicie e confirme a transação!
em.remove(fabricanteTemp);
em.flush(); //executa a instrução nesse momento. } }
Fabricante - DAO
public class FabricanteDAO implements Serializable { ....
public Fabricante buscarPeloCodigo(Long codigo) { return em.find(Fabricante.class, codigo);
} }}
Fabricante - Service
public class CadastroFabricanteService implements Serializable { private FabricanteDAO fabricanteDAO;
public void salvar(Fabricante fabricante) throws NegocioException { //instancie o fabricanteDAO!
//instancie o fabricanteDAO!
if (fabricante.getNome() == null || fabricante.getNome().trim().equals("")) { throw new NegocioException("O nome do fabricante é obrigatório"); }
this.fabricanteDAO.salvar(fabricante); }
Relacionamento Muitos-Para-Um
import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.ManyToOne; @Entitypublic class ModeloCarro {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name=“id”)
private Long codigo; private String descricao;
@ManyToOne
private Fabricante fabricante; //gerar os gets e o sets}
Você pode mudar o nome do relacionamento no banco de dados inserindo a anotação:
Relacionamento Muitos-Para-Muitos
@Entity
public class Carro {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long codigo; private String placa; private String placa; private String cor; private String chassi;
private BigDecimal valorDiaria;
@ManyToOne
@JoinColumn(name="codigo_modelo")
private ModeloCarro modelo;
@ManyToMany
private List<Acessorio> acessorios; //gerar os gets e o sets
}
Relacionamento Muitos-Para-Muitos
Customizando o relacionamento
-"Name" irá definir o nome da tabela que faz o relacionamento.
-A propriedade "joinColumns" recebe um array de @JoinColumn, pois se
-A propriedade "joinColumns" recebe um array de @JoinColumn, pois se
tivermos um relacionamento com chaves compostas, teríamos que usar mais
de uma @JoinColumn.
-A propriedades "inverseJoinColumns", que também recebe um array de
@JoinColumn
irá configurar a propriedade no sentido inverso, ou seja para
Acessório.
Relacionamento Um-Para-Um
@Entity
@Table(name="apolice_seguro")
public class ApoliceSeguro {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY) @GeneratedValue(strategy=GenerationType.IDENTITY)
private Long codigo;
private BigDecimal valorFranquia; private Boolean protecaoTerceiro;
private Boolean protecaoCausasNaturais; private Boolean protecaoRoubo;
//gerar os gets e o sets }
Relacionamento Um-Para-Um
@Entity
public class Aluguel {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY) Optamos por não @GeneratedValue(strategy=GenerationType.IDENTITY)
private Long codigo;
private BigDecimal valorTotal;
@ManyToOne
@JoinColumn(name="codigo_carro")
private Carro carro;
@OneToOne(cascade=CascadeType.ALL)
@JoinColumn(name="codigo_apolice_seguro")
private ApoliceSeguro apoliceSeguro; //gerar os gets e o sets
}
Optamos por não construir um DAO para
Apólice, assim a classe Aluguel deve propagar
a operação para o relacionamento 1-to-1
Relacionamento Um-Para-Muitos
@Entitypublic class Carro { ...
@OneToMany(mapedBy=“carro”)
private List<Aluguel> alugueis; ...
... }
Deve-se informar que o mapeamento foi
feito pela
propriedade “carro” na classe Aluguel
Inicialização Tardia (lazy-loading) e
Ansiosa (eager-loading)
• Eager-loading – ao carregar os dados de um carro, todos os
acessórios também serão recuperados.
@ManyToMany(fetch=FetchType.EAGER)
private List<Acessorio> acessorios;
Lazy-loading – carregue os acessórios somente quando
Lazy-loading – carregue os acessórios somente quando
precisar, ou seja, quando chamar o “getAcessorios”
@ManyToMany(fetch=FetchType.LAZY)
Mapeando enumerações
public enumCategoria {
HATCH_COMPACTO, HATCH_MEDIO, SEDAN_COMPACTO, SEDAN_MEDIO, SEDAN_GRANDE, MINIVAN, ESPORTIVO, UTILITARIO_COMERCIAL }
@Entity
public class ModeloCarro { public class ModeloCarro {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long codigo; private String descricao;
@ManyToOne
@JoinColumn(name="codigo_fabricante")
private Fabricante fabricante;
@Enumerated(EnumType.STRING) ou (EnumType.ORDINAL)
private Categoria categoria \\ gets e sets...}
Mapeando datas
@Entitypublic class Aluguel {
@GeneratedValue(strategy=GenerationType.IDENTITY)
...
@Temporal(TemporalType.DATE)
private Calendar dataPedido;
@Temporal(TemporalType.TIMESTAMP) @Temporal(TemporalType.TIMESTAMP)
private Date dataEntrega;
@Temporal(TemporalType.TIMESTAMP)
private Date dataDevolucao; ...
Mapeando herança com tabela única
@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name="TIPO_PESSOA“,
discriminatorType=DiscriminatorType.STRING)
public abstract class Pessoa {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long codigo; private Long codigo; private String nome;
@Temporal(TemporalType.DATE)
private Date dataNascimento; private String cpf
\\ gets e sets... }
Mapeando herança com tabela única
@Entity
@DiscriminatorValue(“MOTORISTA“)
public Motorista extends Pessoa { private String numeroCNH;
public String getNumeroCNH() { return numeroCNH;
return numeroCNH; }
public void setNumeroCNH(String numeroCNH) { this.numeroCNH = numeroCNH;
} }
Mapeando herança com Join
@Entity@Inheritance(strategy=InheritanceType.JOINED) @DiscriminatorColumn(name="TIPO_PESSOA“,
discriminatorType=DiscriminatorType.INTEGER)
public abstract class Pessoa { ...
}
@Entity
@DiscriminatorValue("1")
public class Motorista extends Pessoa { ...
}
- Nesta estratégia cada entidade terá sua própria tabela.
- A tabela "pessoa" tem código, cpf, data_nascimento e nome. A tabela "motorista" tem o numeroCNH e um código.
- Essa opção pode deixar a manipulação mais lenta, pois as instruções são mais complexas para o JPA gerenciar.
Introdução a Java Persistence Query
Language (JPQL)
Introdução a Java Persistence Query
Language (JPQL)
public void static main (String[] args) { ....
EntityManager em = emf.createEntityManager(); List<String> nomesDosFabricantes =
em.createQuery("select f.nome from Fabricante f", String.class) .getResultList(); for (String nome : nomesDosFabricantes) { System.out.println("Nome: " + nome); } for (String nome : nomesDosFabricantes) { System.out.println("Nome: " + nome); } List<Fabricantes> fabricantes =
em.createQuery("from Fabricante where codigo > 4", Fabricante.class) .getResultList(); for (Fabricante f : fabricantes) { System.out.println("Nome: " + f.getNome() ); }
Selecionando uma entidade de uma
entidade
public void static main (String[] args) { ....
// Consulta fabricantes pelo modelo do carro
List<String> nomesDosFabricantes =
em.createQuery(“select mc.fabricante.nome from ModeloCarro mc",
String.class) .getResultList(); for (String nome : nomesDosFabricantes) { System.out.println("Nome: " + nome); } for (String nome : nomesDosFabricantes) { System.out.println("Nome: " + nome); } }
Filtrando resultados
public void static main (String[] args) { ....
// Consulta modelos filtrados por um determinado fabricante
List<String> nomesDosModelos =
em.createQuery(“select mc.descricao from ModeloCarro mc “+
“where mc.fabricante.nome = ‘Honda’“, String.class) .getResultList(); for (String nome : nomesDosModelos) { System.out.println("Nome: " + nome); }
for (String nome : nomesDosModelos) { System.out.println("Nome: " + nome); }
// Consulta os modelos, de um fabricante que pertença a algumas categorias específicas
List<String> nomesDosModelos =
em.createQuery(“select mc.descricao from ModeloCarro mc “+ “where mc.fabricante.nome = ‘Honda’ and“+
“mc.categoria in (‘MINIVAN,’ESPORTIVO’) “, String.class) .getResultList(); for (String nome : nomesDosModelos) { System.out.println("Nome: " + nome); }
Filtrando resultados
public void static main (String[] args) { ....
// Uso do like
// Consulta os modelos, de um fabricante que pertença a algumas categorias específicas
List<String> nomesDosModelos =
em.createQuery(“select mc.descricao from ModeloCarro mc “+ “where mc.fabricante.nome = ‘Honda’ and“+
“mc.categoria like ‘HATCH%’“, String.class) .getResultList(); “mc.categoria like ‘HATCH%’“, String.class) .getResultList(); for (String nome : nomesDosModelos) { System.out.println("Nome: " + nome); } }
Trabalhando com projeções
public void static main (String[] args) { ....
// Consulta somente a descrição dos modelos de carros
List<Object[]> resultados =
Estamos usando o método com um parâmetro
List<Object[]> resultados =
em.createQuery(“select mc.descricao, mc.categoria from ModeloCarro mc“) .getResultList();
for (Object[] obj : resultados) { System.out.println(obj[0]+" " + obj[1]); } }
Estamos buscamos Estamos buscamos
apenas alguns campos do objeto
Fazendo Join entre entidades
public void static main (String[] args) { ....
// Dado um modelos de carro vamos buscar todos os acessórios vinculados a eles // Tabelas: ModeloDeCarros >> Carros >> Acessorios (n-to-n)
List<String> acessorios =
em.createQuery(“select a.descricao from Carro c “+ “JOIN c.acessorios a “+
“where c.modelo.descricao = ‘Cruze’ “, “where c.modelo.descricao = ‘Cruze’ “, String.class) .getResultList();
for (String acessorio : acessorios) { System.out.println(" Acessorio: " + acessorio); } }
Queries agregadas
public void static main (String[] args) { ....
// Recuperar o maior valor, a qtde e a média de alugueis // para os carros com mais de um aluguel
List<Object[]> acessorios =
em.createQuery(“select c, count(a), max(a.valorTotal), avg(a.valorTotal) “+ “from Carro c JOIN c.alugueis a “+
“group by c “+ “group by c “+
“having count(a) > 1”) .getResultList(); for (Object[] obj : resultados) {
System.out.println( ((Carro)obj[0]).getModelo().getDescricao() +" " + obj[1]) +" " +
obj[2]) +" " + obj[1]); }
Novos tipos de retorno
•
Vamos melhorar o tipo de retorno do slide anterior criando um
novo objeto na hora da consulta com os valores do select.
•
Cria a classe abaixo dentro de um pacote chamado “info”.
public class AluguelCarroInfo { private Carro carro;
private Long totalAlugueis; private Long totalAlugueis;
private BigDecimal valorMaximo; private BigDecimal valorMedio;
public AluguelCarroInfo(Carro carro, Long totalAlugueis, Number valorMaximo, Number valorMedio) {
this.carro = carro;
this.totalAlugueis = totalAlugueis;
this.valorMaximo = BigDecimal.valueOf(valorMaximo.doubleValue()); this.valorMedio = BigDecimal.valueOf(valorMedio.doubleValue()); } //omitido os gets e sets dos métodos
Queries agregadas e tipos de retorno
public void static main (String[] args) { ....
// Recuperar o maior valor, a qtde e a média de alugueis // para os carros com mais de um aluguel
List< AluguelCarroInfo> resultados =
em.createQuery(“select NEW br.ifmg.modelo.AluguelCarroInfo “+ “( c, count(a), max(a.valorTotal), avg(a.valorTotal) )“+ “from Carro c JOIN c.alugueis a “+
“from Carro c JOIN c.alugueis a “+ “group by c “+
“having count(a) > 1”) .getResultList(); for (AluguelCarroInfo aci : resultados) {
System.out.println("Modelo: " + aci.getCarro().getModelo().getDescricao()); System.out.println("Quantidade de alugueis: " + aci.getTotalAlugueis());
System.out.println("Valor máximo: " + aci.getValorMaximo());
System.out.println("Valor médio: " + aci.getValorMedio()); System.out.println(); }
Passando parâmetros para queries
public void static main (String[] args) { ....
// Recuperar os modelos de carros para um determinado nome de fabricante
String modelo = JOptionPane.showInputDialog(null,”Entre com o nome do fabricante.”); String jpql = "select mc.descricao from ModeloCarro mc " +
"where mc.fabricante.nome = :modelo"; List<String> modelos = em.createQuery(jpql, String.class)
.setParameter("modelo", modelo) .getResultList();
for (String m : modelos) { System.out.println(m); } }
OBS: Existe uma outra maneira de passagem de parâmetro:
-Ao invés de dar nomes para os parâmetro, você pode usar o caracter ‘?’ mais um número de identificação. Ex: where (codigo = ?1) and (cep = ?2). -Para informar os valores use o método setParameter da seguinte forma: .setParameter(1, 1000).setParameter(2, “3550-000”)
Passando parâmetros para queries
(Datas)
public void static main (String[] args) { ....
// Recuperar quantos alugueis foram feitos em um determinado período
String jpql = "select count(a) from Aluguel a " +
"where a.dataDevolucao BETWEEN :inicio AND :fim"; Calendar inicioCalendar = Calendar.getInstance();
inicioCalendar.set(2013, 0, 28, 7, 0); // 24 de Janeiro de 2013 7:00 horas
inicioCalendar.set(2013, 0, 28, 7, 0); // 24 de Janeiro de 2013 7:00 horas
Date inicio = inicioCalendar.getTime();
Calendar fimCalendar = Calendar.getInstance();
fimCalendar.set(2013, 11, 30, 18, 0); // 25 de Dezembro de 2013 18:00 horas
Date fim = fimCalendar.getTime();
Long quantidade = em.createQuery(jpql, Long.class)
.setParameter("inicio", inicio, TemporalType.TIMESTAMP) .setParameter(“fim", fim, TemporalType.TIMESTAMP) .getSingleResult();
System.out.println("Quantidade de devoluções: " + quantidade); }
Named Query
•
São consultas JPQL prontas que recebem nomes únicos.
•
Elas são definidas nas classes de modelos.
@Entity
@NamedQueries({
@NamedQuery(name="buscarTodosCarros", query="select c from Carro c"), @NamedQuery(name="buscarCarroComAcessorios",
query="select c from Carro c JOIN c.acessorios a " + query="select c from Carro c JOIN c.acessorios a " +
" where c.codigo = :codigo") })
public class Carro { ... }
public void static main (String[] args) { ....
// Recuperar todos os carros
List< Carro> resultados = em.createNamedQuery(“buscarTodosCarros").getResultList();
// Recuperar os acessórios de um determinado carros
Carro car = em.createNamedQuery(“buscarCarroComAcessorios", Carro.class) .setParameter("codigo", codigo) .getSingleResult();
Named Queries em arquivos externos
1. Crie o arquivo com as Named Queries:
<?xmlversion="1.0" encoding="UTF-8"?><entity-mappingsxmlns="http://xmlns.jcp.org/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm
http://xmlns.jcp.org/xml/ns/persistence/orm_2_1.xsd"
version="2.1">
<named-queryname="Lancamento.descricoesQueContem"> <named-queryname="Lancamento.descricoesQueContem">
<query>
SELECT distinct descricao FROM Lancamento WHERE upper(descricao) like upper(:descricao) </query>
</named-query>
<named-queryname="Lancamento.todos"> <query> FROM Lancamento l </query> </named-query> </entity-mappings>
Named Queries em arquivos externos
2. Indique no “persistence.xml” aonde estão salvos os arquivos:
…
<persistence-unit name="financeiroPU" transaction-type="RESOURCE_LOCAL"> <mapping-file>META-INF/consultas/pessoas.xml</mapping-file>
<mapping-file>META-INF/consultas/lancamentos.xml</mapping-file> ...
3. Utilize as Named Queries normalmente:
...
return em.createNamedQuery("Lancamento.descricoesQueContem", String.class) .setParameter("descricao", "%água%")
Exercício - Movendo os objetos pelo
ciclo de vida
•
criar um objeto, para o EntityManager ele estará no estado "new“.
•
passar o objeto para o estado "managed“ (métodos persit ou merge).
•
O objeto foi salvo, mas continua no estado "managed“, vamos desconectar
esse objeto do EntityManager.
Exercício - Movendo os objetos pelo
ciclo de vida
Exercício - Movendo os objetos pelo
ciclo de vida
•
Colocar o objeto gerenciado novamente (método merge find)
Exercício - Movendo os objetos pelo
ciclo de vida
•
Reconectar o objeto removido com o método merge. Repare um detalhe
muito importante desse método: ele devolve um outro objeto!
Isso significa que se você fizer a alteração no cliente1, nada será alterado no
banco de dados! Agora, se fizer as alterações no objeto retornado, as
alterações irão para o banco. Veja o código abaixo:
Ciclo de vida e Estados dos objetos
Chamando funções de Callback
@Entity
public class Carro { ....
@Temporal(TemporalType.TIMESTAMP)
private Date dataCriacao; private Date dataCriacao;
@Temporal(TemporalType.TIMESTAMP)
private Date dataModificacao;
@PrePersist @PreUpdate
public void configuraDatasCriacaoAlteracao() { this.dataModificacao = new Date();
if (this.dataCriacao == null) {
this.dataCriacao = new Date(); } }
//gerar os gets e o sets para essas duas propriedades }
Criteria - básico
public static void main(String[] args) { ...
// JPQL: from Cliente
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Cliente> criteriaQuery = builder.createQuery(Cliente.class); criteriaQuery.from(Cliente.class);
List<Cliente> = em.createQuery(criteriaQuery).getResultList(); for (Cliente cliente : clientes) {
System.out.println("Código: " + cliente.getCodigo()); System.out.println("Nome: " + cliente.getNome()); } ....
Criteria - básico
public static void main(String[] args) { ...
// JPQL: select c from Cliente where c.nome like = 'Fernando%'
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Cliente> criteriaQuery = builder.createQuery(Cliente.class); Root<Cliente> c = criteriaQuery.from(Cliente.class);
criteriaQuery.select(c); criteriaQuery.select(c);
criteriaQuery.where( builder.like(c.<String>get("nome"), "Fernando%") ); List<Cliente> = em.createQuery(criteriaQuery).getResultList();
for (Cliente cliente : clientes) {
System.out.println("Código: " + cliente.getCodigo()); System.out.println("Nome: " + cliente.getNome()); } ....
Gravação em cascata
•
Se tentarmos incluir um carro com um modelo novo, ou seja, ainda
não persistido. O JPA retornaria um erro. Para que isso funcione
temos que configurar o “cascate” do relacionamento entre
carro/modelo.
public class Carro { public class Carro { ....
@ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.MERGE) @JoinColumn(name="codigo_modelo")
private ModeloCarro modelo; ....
Exclusão em cascata
•
Se quisermos apagar um carro que tenha alugueis feitos. Ou seja,
fazer a exclusão em cascata com relacionamentos n-to-n
@Entity
public class Carro { ....
....
@ManyToOne
@JoinColumn(name="codigo_modelo")
private ModeloCarro modelo;
@OneToMany(mapedby=“carro”, cascade=CascadeType.REMOVE)
private List<Aluguel> alugueis; ...
Exclusão de objetos órfãos
•
Se quisermos apagar um determinado aluguel diretamente na
entidade carro. Exemplo:
public static void main(String[] args) { ...
Carro car1 = em.find(Carro.class, 1L);
Nesse caso, a exclusão não aconteceria, pois a configuração feita no slide anterior só acontece quando
public class Carro {
.... // Para excluir objetos órfãos existe uma anotação própria
@OneToMany(mapedby=“carro”, cascade=CascadeType.PERSIST, orphanRemoval=true)
private List<Aluguel> alugueis; ... } em.getTransaction().begin(); car1.getAlugueis().remove(0); em.getTransaction().commit(); .... }
anterior só acontece quando excluímos toda a entidade,
aqui, o objeto do
Mapeando chaves compostas
•
Exemplo: para identificar um veículo unicamente é necessário o
número da placa e a cidade.
1. Crie uma classe com os campos da chave composta.
@Embeddable
public class VeiculoID implements Serializable{ public class VeiculoID implements Serializable{ private String placa;
private String cidade; public VeiculoId() { }
...
Mapeando chaves compostas
2. Crie a classe que tem a chave composta.
@Entity
public class Veiculo implements Serializable{
@EmbeddedId
private VeiculoId codigo; private VeiculoId codigo; private String fabricante; private String modelo; public Veiculo() {
} ...
Mapeando chaves compostas
3. Inserindo um objeto com chave composta.
public class ExemploChaveComposta { public static void main(String[] args) {
...
Veiculo veiculo = new Veiculo(); Veiculo veiculo = new Veiculo();
veiculo.setCodigo(new VeiculoId("ABC-1235", "Ouro Preto")); veiculo.setFabricante("Volks"); veiculo.setModelo("Gol"); em.getTransaction().begin(); em.persist(veiculo); em.getTransaction().commit(); em.close(); } }
Mapeando chaves compostas
3. Pesquisando um objeto com chave composta.
public class ExemploChaveComposta { public static void main(String[] args) {
...
VeiculoId codigo = new VeiculoId("ABC-1234", "Rio Claro"); Veiculo v = em.find(Veiculo.class, codigo);
System.out.println("Veiculo " + v.getCodigo().getPlaca() + " - " + v.getCodigo().getCidade() + " - Fabricante: " + v.getFabricante()); em.close(); } }
Mapeando objetos embutidos
•
Talvez, você opte por criar uma tabela grande, ao invés de usar
joins, mas ainda deseja separar os objetos seguindo os princípios de
orientação a objetos.
•
Exemplo: teremos uma classe chamada Proprietário que estará
dentro da classe Veiculo (criada no slide anterior)
@Embeddable @Embeddable
public class Proprietario implements Serializable{ ....
@Column(name="nome_proprietario")
private String nome; private String telefone; private String email; public Proprietario() { }
...
Mapeando objetos embutidos
@Entitypublic class Veiculo implements Serializable{ ....
@Embedded
private Proprietario proprietario; ...
} //crie o get e set
public class ExemploChaveComposta { public static void main(String[] args) {
...
Veiculo veiculo = new Veiculo();
veiculo.setCodigo(new VeiculoId("ABC-1223", “Belo Horizonte")); veiculo.setFabricante("Volks");
veiculo.setModelo("Gol");
veiculo.setProprietario(new Proprietario(“João”, ”123-9876”, ”john@hit.com”) ); em.getTransaction().begin();
em.persist(veiculo);
Propriedades transientes
•
Informar que uma propriedade ou método não seja mapeando para
o banco de dados.
•
Exemplo: criar um método “getInfo” que retorne os dados do
objeto imitando o toString.
@Entity @Entity
public class Veiculo implements Serializable{ ...
@Transient
Public String getInfo() {
return "Placa: " + getCodigo().getPlaca() + ". Fabricante: " + getFabricante() + ". Modelo: " + getModelo();
} ... }
Coleções de tipos básicos
•
Podemos simular uma entidade fraca através de uma lista. Assim o
mapeamento pode ser feito da seguinte forma:
@Entity
A entidade fraca terá a chave estrangeira chamada
‘cod_proprietario’ e o campo ‘numero_telefone’ @Entity
public class Proprietario implements Serializable{ ...
@ElementCollection
@CollectionTable(name="proprietario_telefones",
joinColumns=@JoinColumn(name="cod_proprietario")) @Column(name="numero_telefone")
private List<String> telefones = new ArrayList<>(); ...
} //crie os gets e sets, além do equal e o hashcode e um construtor sem parâmetro
Coleções de tipos básicos
Cadastro e consulta
public class ExemploColecoes {
public static void main(String[] args) { ...
em.getTransaction().begin();
Proprietario proprietario = new Proprietario(); proprietario.setNome("João"); proprietario.getTelefones().add("(34) 1234-5678"); proprietario.getTelefones().add("(34) 1234-5678"); proprietario.getTelefones().add("(11) 9876-5432"); em.persist(proprietario); em.getTransaction().commit();
Proprietario proprietario = em.find(Proprietario.class, 1L); System.out.println("Nome: " + proprietario.getNome()); for (String telefone : proprietario.getTelefones()) {
System.out.println("Telefone: " + telefone); }
em.close(); } }
Coleções com objetos embutidos
@Embeddablepublic class Telefone implements Serializable{ ...
private String prefixo; private String numero; private String ramal; ...
} //crie os gets e sets, além do equal e o hashcode e um construtor sem parâmetro
Entidade fraca
public class Proprietario implements Serializable{ ... @ElementCollection @CollectionTable(name="proprietario_telefones", joinColumns=@JoinColumn(name="cod_proprietario")) @AttributeOverrides({@AttributeOverride(name="numero", column=@Column(name="num_telefone"))})
private List<Telefone> telefones = new ArrayList<>(); ...
} //crie os gets e sets, além do equal e o hashcode e um construtor sem parâmetro
Configura a chave estrangeira da entidade fraca e o nome de
uma coluna
Coleções com objetos embutidos
Cadastro e consulta
public class ExemploColecoesDeEntidades { public static void main(String[] args) {
...
em.getTransaction().begin();
Proprietario proprietario = new Proprietario(); proprietario.setNome("João"); proprietario.getTelefones().add(new Telefone("34", "1234-5678", "104")); proprietario.getTelefones().add(new Telefone("34", "1234-5678", "104")); proprietario.getTelefones().add(new Telefone("33", “4321-5678", "")); em.persist(proprietario); em.getTransaction().commit();
Proprietario proprietario = em.find(Proprietario.class, 1L); System.out.println("Nome: " + proprietario.getNome()); for (Telefone tel : proprietario.getTelefones()) {
System.out.println("Telefone: " + tel.getPrefixo() + “ “ + tel.getNumero() ); }
em.close(); } }
Objetos grandes
public class Carro { ....
@Lob
private byte[] foto;
.... } //get and set para a propriedade foto
•
Para salvar uma imagem no banco de dados basta mapear uma
propriedade como Lob (large object):
.... } //get and set para a propriedade foto
public class CarroDAO { ....
public byte[] imagemEmArray(String pathImage) throws IOException{ BufferedImage imagem = ImageIO.read(new File(pathImage)); ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(imagem, "png", baos); return baos.toByteArray();}
.... }
Objetos grandes
public class CarroDAO { ....
public ImageIcon buscaFoto(Long codigo) throws IOException { Carro carro = em.find(Carro.class, codigo);
if (carro.getFoto() != null) {
BufferedImage img = ImageIO.read(new ByteArrayInputStream(carro.getFoto()));
Para exibir uma imagem, converta o array de bytes em uma imagem
BufferedImage img = ImageIO.read(new ByteArrayInputStream(carro.getFoto())); return new ImageIcon(img);
} else
return null; }
.... }
Use o objeto ImageIcon como desejar:
public static void main(String[] args) {
... JOptionPane.showMessageDialog(null, new JLabel( carroDAO.buscaFoto(3L) ));... }
Mais detalhes de @Column
@Entitypublic class Aluguel { ... @Column(name=”dtPedido”, nullable=true, unique=false, insertable=true, insertable=true, updatable=true)
private Calendar dataPedido;
@Column(lenght=100)
private String nome ...
Problema n+1
•
Lembre-se que a propriedade ‘ModeloCarro’ é FetchType.LAZY na
entidade carro. Agora imagine os passos abaixo:
– Ao executar um query (‘createquery’ ), uma consulta é feita.
– Mas para cada chamada do método ‘getModeloCarro’, mais uma consulta é executada! Isso não é eficiente!!
Exemplo:
Exemplo:
List<Carro> carros = em.createQuery("from Carro", Carro.class).getResultList(); for (Carro carro : carros) {
System.out.println(carro.getPlaca() + " - " +
carro.getModeloCarro().getDescricao()); }
Problema n+1
Observe as consultas para dois
carros cadastros no BD:
Problema n+1 - Resolvido
List<Carro> carros =
em.createQuery("from Carro c inner join fetch c.modeloCarro",
Carro.class).getResultList(); for (Carro carro : carros) {
System.out.println(carro.getPlaca() + " - " +
carro.getModeloCarro().getDescricao()); carro.getModeloCarro().getDescricao()); }
Cache de primeiro nível
•
Cache padrão – enquanto não fechar o EntityManager os dados
ficam armazenados em memória.
List<Carro> carros = em.createQuery("from Carro", Carro.class).getResultList(); for (Carro carro : carros)
for (Carro carro : carros)
System.out.println(carro.getPlaca() ); System.out.println("---"); Carro carro = em.find(Carro.class, 3L);
System.out.println(carro.getCodigo());
Cache de primeiro nível
•
Quanto o EntityManager for fechado o cache é limpo
List<Carro> carros =
em.createQuery("from Carro", Carro.class).
getResultList(); for (Carro carro : carros)
for (Carro carro : carros)
System.out.println(carro.getPlaca() ); em.close();
System.out.println("---"); em = FabricaEntity.getEntityManager(); Carro carro = em.find(Carro.class, 3L); System.out.println(carro.getCodigo());
Consultas nativas
....
List<Carro> carros =
em.createNativeQuery("select * from Carro", Carro.class). getResultList();
•
Usado para consultas que não podem ser representadas pela JPQL e
Criteria. Deve ser evitado pois não representam o mundo OO.
em.createNativeQuery("select * from Carro", Carro.class). getResultList(); for (Carro carro : carros) {
System.out.println(carro.getPlaca() ); }
Tipos de cache do Hibernate
Tipos de cache do Hibernate
•
Coloca os objetos na memória RAM independente do EntityManager
Cache de 2º nível e de query
Existem pacotes para gerenciar o cache de forma
•
Para habilitar o cache de 2º nível no hibernate, basta configurar o
percistence.xml:
<property name="hibernate.cache.use_second_level_cache" value="true"/> <property name="hibernate.cache.region.factory_class"
value="org.hibernate.testing.cache.CachingRegionFactory"/>
cache de forma mais profissional.
Tipos de cache do Hibernate
Exemplo: .... @Entity @Table(name = "cidade") @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)public class Cidade implements Serializable { public class Cidade implements Serializable { ...
}
@Entity
@Table(name = “estado")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Estado implements Serializable { ...
}
NONSTRICT_READ_WRITE – atualizações feitas eventualmente READ _ONLY – não haverá atualizações
O pattern DAO
Criando um DAO Genérico
public interface GenericDAO<T, ID extends Serializable> { public T buscarPeloCodigo(ID id);
public void salvar(T entidade); }
import java.lang.reflect.ParameterizedType;
1º Passo) Criar uma interface:
2º Passo) Criar uma classe que implemente a interface do Passo 1: import java.lang.reflect.ParameterizedType;
public class HibernateGenericDAO<T, ID extends Serializable> implements GenericDAO<T, ID> { private EntityManager em;
private Class<T> classeEntidade; public HibernateGenericDAO() {
em = FabricaEntity.getEntityManager();
this.classeEntidade = (Class<T>) ((ParameterizedType)
getClass().getGenericSuperclass()) .getActualTypeArguments()[0]; }
Criando um DAO Genérico
...
@Override
public T buscarPeloCodigo(ID id) { return em.find(classeEntidade, id); }
@Override
public void salvar(T entidade) { public void salvar(T entidade) {
em.getTransaction().begin(); em.merge(entidade);
em.getTransaction().commit(); }
protected EntityManager getEntityManager() { return em;
} }
Criando um DAO Genérico
public class PedidoDAO extends HibernateGenericDAO<Pedido, Long> implements
Serializable { public List<ValorTotalVendaDoDia> buscarValorTotalVendaDoDia() {
return getEntityManager().createQuery("select "
+ "NEW ifmg.ValorTotalVendaDoDia(p.dataVenda, sum(p.valorTotal)) " +
3º Passo) Crie uma classe que estende HibernateGenericDAO:
+ "NEW ifmg.ValorTotalVendaDoDia(p.dataVenda, sum(p.valorTotal)) " + "from Pedido p " +
"group by p.dataVenda", ValorTotalVendaDoDia.class) .getResultList(); }
}
4º Passo) Use a classe genérica à vontade:
PedidoDAO pedDAO = new PedidoDAO(); pedDAO. Salvar(pedido);
Trabalhando com procedures
create table pedido (
codigo bigint primary key auto_increment, valor decimal(10,2),
status varchar(50) );
create table devolucao (
codigo bigint primary key auto_increment, codigo bigint primary key auto_increment, codigo_pedido bigint,
status varchar(50) );
create table entrega (
codigo bigint primary key auto_increment, codigo_pedido bigint,
previsao date );
Trabalhando com procedures
DELIMITER $$
CREATE PROCEDURE analisar (vCodigo bigint, OUT vAnalise varchar(50)) BEGIN
DECLARE vStatus varchar(50);
select status into vStatus from pedido where codigo = vCodigo; START TRANSACTION;
START TRANSACTION;
if vStatus = 'CANCELADO' then
insert into devolucao (codigo_pedido, status) values (vCodigo, 'PENDENTE'); set vAnalise = 'DEVOLUCAO';
elseif vStatus = 'APROVADO' then
insert into entrega (codigo_pedido, previsao) values (vCodigo, now()); set vAnalise = 'ENTREGA';
end if;
COMMIT; END $$
Trabalhando com procedures
public static void main(String[] args) { ....
StoredProcedureQuery sp = em.createStoredProcedureQuery("analisar");
sp.registerStoredProcedureParameter(" vCodigo ", Long.class, ParameterMode.IN); sp.setParameter(" vCodigo ", 1L);
sp.registerStoredProcedureParameter("analise", String.class, ParameterMode.OUT); sp.execute();
String analise = (String) sp.getOutputParameterValue("analise"); System.out.println("A análise foi: " + analise);