Академический Документы
Профессиональный Документы
Культура Документы
Quando estvamos configurando a Movimentacao para ser gerenciada pelo Hibernate, vimos que bastou anotarmos o atributo conta com @ManyToOne para estabelecer a relao de muitos para um. Com isso conseguimos recuperar a conta associada movimentao simplesmente invocando o getConta(), por exemplo: Vamos mostrar isso uma criando uma nova classe TesteMovimentacaoConta dentro do package financas.teste. No mtodo main abrimos o EntityManager e carregamos a movimentao com o id 2. Cuidado, o registro deve existir no banco. Falta mostrar o resultado no console. Com a movimentao carregada vamos imprimir a conta associada usando o relacionamento
movimentacao.getConta().getTitular():
public class TesteMovimentacaoConta { public static void main(String[] args) { EntityManager manager = new JPAUtil().getEntityManager(); Movimentacao movimentacao = manager.find(Movimentacao.class, 2); System.out.println("Titular da conta: " movimentacao.getConta().getTitular()); } }
Ao executar, ser impresso o nome do titular. Repare que no foi necessrio se preocupar com o join ou chave estrangeira, usamos o nosso modelo OO para navegar no relacionamento.
package br.com.caelum.financas.modelo; // imports omitidos @Entity public class Conta { // demais atributos omitidos private List<Movimentacao> movimentacoes; public List<Movimentacao> getMovimentacoes() { return movimentacoes; } // demais getters e setters omitidos }
Mas deixando dessa maneira, ainda no conseguimos falar para o Hibernate trazer todas movimentaes da conta. Perceba que simplesmente criamos um atributo, nada de mais. Assim como anotamos com @ManyToOne quando queremos indicar um relacionamento de muitos para um, precisamos de uma anotao para indicar um relacionamento de um para muitos. Ela se chama @OneToMany. Uma conta possui vrias movimentaes.
O cdigo ficaria:
@Entity public class Conta { // demais atributos omitidos @OneToMany private List<Movimentacao> movimentacoes;
movimentaes.
Na tabela de registros podemos ver que existem sim 4 movimentaes da conta com o id 1. Algo est errado com o nosso mapeamento.
Aqui encontramos uma clara diferena entre o mundo relacional e o orientado a objetos. Para o nosso banco de dados, quando mapeamos a chave estrangeira na tabela de movimentaes, automaticamente j est estabelecida a relao movimentao para conta e vice versa. Mas na orientao a objetos, essa relao no implcita. Repare que mapeamos o relacionamento na classe Movimentacao e na Conta. De certo modo isso pode ser um problema, j que o JPA vai entender que os relacionamentos so independentes.
Para ela existem duas relaes, uma de muitos para um da movimentao para a
conta e outra de um para muitos daconta para as movimentaes. Para representar
esse novo mapeamento, o Hibernate cria uma nova tabelaConta_Movimentacao concorrendo com o antigo relacionamento da FK conta_id.
@Entity public class Conta { // outros atributos aqui @OneToMany(mappedBy="conta") private List<Movimentacao> movimentacoes; // getters e setters }
mtodocreateQuery(..) do EntityManager faremos uma consulta no banco que retornar uma lista com todas as contas cadastradas.
public class TesteMovimentacaoConta { public static void main(String[] args) { EntityManager manager = new JPAUtil().getEntityManager(); Query query = manager.createQuery("select c from Conta c"); List<Conta> contas = query.getResultList(); } }
Nesse mesmo mtodo, vamos iterar na lista contas e para cada conta, imprimiremos o tamanho da lista de movimentaes com o mtodo size(), ou seja o nmero de movimentaes:
public class TesteMovimentacaoConta { public static void main(String[] args) { // cdigo com a query for (Conta conta : contas) { System.out.println("Nmero de movimentaes: " + conta.getMovimentacoes().size() ); } } }
Mas a cada chamada do mtodo getMovimentacoes(), ele gera um novo SQL e executa uma nova consulta no banco, agora para as movimentaes de cada conta:
select movimentac0.conta_id as conta6_0_1, movimentac0.id as id1_1,
movimentac0.id as id1_0, movimentac0.conta_id as conta6_1_0, movimentac0.data as data1_0, movimentac0.descricao as descricao1_0, movimentac0.valor as valor1_0 movimentac0.tipoMovimentacao as tipoMovi4_1_0, from Movimentacao movimentac0 where
movimentac0.conta_id=?
Tivemos logo no incio uma primeira consulta que buscou as contas no banco e retornou os objetos. Para cada uma das contas, tivemos uma consulta extra para buscar suas movimentaes, e por isso dizemos que tivemos N+1 consultas. N o nmero de objetos retornados por uma consulta original. Neste caso, se tivermos 5 contas cadastradas, faremos 1 consulta para recuperar todas essas contas, mais 5 novas consultas para buscas as movimentaes de cada uma delas (5+1), ou seja, 6 consultas ao banco.
public class TesteMovimentacaoConta { public static void main(String[] args) { // cdigos omitidos Query query = manager
Query query = manager .createQuery("select distinct c from Conta c join fetch c.movimentacoes");
Mas ateno, o contrrio no possvel. No tem como definirmos um relacionamento como eager e fazer uma consulta lazy para carregar um simples combo, por exemplo. Por isso geralmente deixamos o relacionamento lazy e caso precisemos foramos o carregamento somente em uma consulta.
O FetchType.EAGER
Uma outra forma de fazer isso transformando todo o relacionamento em eager, fazendo o parmetro fetch do mapeamento @OneToMany da entidade
Conta, receber o valor FetchType.EAGER. Mas isso na maioria dos casos