Вы находитесь на странице: 1из 8

Fazendo buscas com funes de agregao

Vamos continuar com o projeto finanas. J vimos como montar consultas usando o JPQL. Era preciso abrir um EntityManager para criar uma Query e definir a consulta atravs do JPQL. Com a Query em mos, preenchemos os parmetros e a executamos com o mtodo getResultList():

EntityManager manager = new JPAUtil().getEntityManager(); Query query = manager.createQuery( "select m from Movimentacao m" + "where m.conta.id=:pId" +" and m.tipoMovimentacao = :pTipo" +" order by m.valor desc"); query.setParameter("pId", 3); query.setParameter("pTipo", TipoMovimentacao.ENTRADA); List<Movimentacao> movimentacoes = query.getResultList();

Somando os valores pela conta


Podemos incrementar nossas consultas com algumas funes bem teis. Vamos fazer um teste onde conseguiremos ver qual o valor total movimentado em uma determinada conta. Primeiramente, vamos criar uma classe chamada TesteConsultaFuncoes. Para facilitar, copiamos a classe TesteConsulta e a renomeamos. No mtodo main, vamos precisar de um EntityManager e um objeto do tipo Conta (no nosso caso com o id
3).

Agora vamos montar a query que seleciona todas as

movimentaes dado uma conta e um tipo de movimentao:

String jpql = "select m from Movimentacao m where m.conta=:pConta " + "and m.tipoMovimentacao=:pTipo";

Mas

dessa

maneira

estaramos

retornando

lista

com

todas

as

movimentaes, e no bem isso que queremos. Queremos apenas a soma dos valores e, para isso, vamos usar a funo de agregao sum:

String jpql ="select sum(m.valor) from Movimentacao m where m.conta=:pConta " + "and m.tipoMovimentacao=:pTipo"; Anteriormente, usamos o mtodo getResultList() do EntityManager para retornar uma lista com os objetos selecionados pela nossa consulta, mas nesse momento ele no nos serve. Queremos retornar apenas um nico valor, no caso o somatrio dos valores. Para isso, vamos usar o mtodo

getSingleResult() que devolve um nico valor, no caso a soma como


BigDecimal.

preciso usar um cast para o cdigo compilar e no podemos

esquecer de imprimir o valor no console: public class TestaJpqlAvancada { public static void main(String[] args) { EntityManager manager = new JPAUtil().getEntityManager(); Conta conta = new Conta(); conta.setId(3); String jpql = "select sum(m.valor) from Movimentacao m where m.conta=:pConta " + "and m.tipoMovimentacao=:pTipo"; Query query = manager.createQuery(jpql); query.setParameter("pConta", conta); query.setParameter("pTipo", TipoMovimentacao.SAIDA); BigDecimal resultado = (BigDecimal) query.getSingleResult(); System.out.println("Total movimentado ..: R$ " + resultado); } }

Ao executar o cdigo gerado o SQL com a funo SUM.

Melhorando a busca TypedQuery


Voltando para nosso cdigo, o cast foi necessrio pois o mtodo getSingleResult(), da interface Query, retorna umObject. A varivel resultado precisa ser um BigDecimal, consequentemente aplicamos um cast:
BigDecimal resultado = (BigDecimal) query.getSingleResult();

Para deixar o nosso cdigo um pouco mais elegante o JPA2 trouxe um novo tipo de objeto para realizarmos queries type safety (que previne erros de tipos de dados), o TypedQuery. Com ele, conseguimos definir o tipo do objeto que vai ser retornado pela nossa query e, com isso, no precisamos ficar fazendo os casts. Para conseguirmos esse objeto, temos que usar a sobrecarga do mtodo createQuery do EntityManager que recebe um segundo parmetro indicando o tipo de retorno: public class TestaJpqlAvancada { public static void main(String[] args) { // trechos omitidos TypedQuery<BigDecimal> query = manager.createQuery(jpql, BigDecimal.class); // trechos omitidos BigDecimal resultado = query.getSingleResult(); System.out.println("Total movimentado ..: R$ " + resultado); } } Repare que agora no precisamos mais fazer o cast, o prprio TypedQuery consegue inferir o tipo de retorno. Ao executar tudo continua funcionando.

Buscando a mdia dos valores


E se decidssemos que agora queremos saber o valor mdio dos gastos de uma determinada conta? Teoricamente basta apenas substituir a funo de agregao SUM por AVG. Mas um detalhe da funo AVG que ela retorna um objeto do tipo Double. Ento, precisaramos substituir BigDecimal por Double para a consulta funcionar. O novo cdigo ficaria assim: package br.com.caelum.financas.teste; // imports omitidos public class TestaJpqlAvancada { public static void main(String[] args) { // trechos omitidos String jpql = "select avg(m.valor) from Movimentacao m where m.conta=:pConta " + "and m.tipoMovimentacao=:pTipo"; TypedQuery<Double> query = manager.createQuery(jpql, Double.class); query.setParameter("pConta", conta); query.setParameter("pTipo", TipoMovimentacao.SAIDA); Double resultado = query.getSingleResult(); System.out.println("Total movimentado ..: R$ " + resultado); } }

Vamos executar uma vez para ver o SQL gerado. Aqui tambm, o Hibernate aplica a funo AVG corretamente.

Organize suas consultas com o Data Access Object


J vimos como criar nossas consultas e como lidamos com elas no cdigo Java. Mas talvez voc esteja pensando: Meu JPQL est todo espalhado pelo projeto! Sim, isso verdade. Quando utilizamos DAOs (Data Access Object), a maior parte dos nossos cdigos de manipulao de dados (inseres, consultas, etc) ficam concentradas neles. Para mostrar o uso de um DAO vamos extrair uma classe MovimentacaoDao que encapsula o cdigo de consulta. Novamente aproveitamos o Eclipse e escrevemos o cdigo como se ele j existisse. Nesse caso queremos instanciar uma classe MovimentacaoDao. No construtor vamos passar o EntityManager:

MovimentacaoDao dao = new MovimentacaoDao(manager); Com essa linha o Eclipse j sabe como criar a classe e o construtor, basta apertar ctrl + 1 para o Eclipse ajudar. A classe fica dentro do pacote
br.com.caelum.financas.dao

e guardamos o EntityManager como atributo:

public class MovimentacaoDao { private EntityManager manager; public MovimentacaoDao(EntityManager manager) { this.manager = manager; } } Com a classe criada, vamos us-la como se ela j tivesse mtodos. Queremos um mtodo que devolva um Double com a nossa mdia, e que se chame mediaDaContaPeloTipo(...). O mtodo receber a varivel conta e o tipo da movimentao. Novamente o Eclipse gerar o cdigo na classe. Com o mtodo criado falta colar o cdigo da query e devolver o resultado da pesquisa. Repare que o DAO encapsula todos os detalhes da busca com JPA: public class MovimentacaoDao {

private EntityManager manager; public MovimentacaoDao(EntityManager manager) { this.manager = manager; } public Double mediaDaContaPeloTipo(Conta conta, TipoMovimentacao tipo) { TypedQuery<Double> query = manager.createQuery( "select avg(m.valor) from Movimentacao m where m.conta=:pConta " + " and m.tipoMovimentacao = :pTipo", Double.class); query.setParameter("pConta", conta); query.setParameter("pTipo", tipo); return query.getSingleResult(); } } Por fim, vamos executar o mtodo main para ver se tudo continua funcionando. No h supresas, a mdia impressa no console como esperado.

A alternativa Named Queries


H uma outra forma de organizar as queries. Com o JPA, trabalhamos mais orientados objetos, fazendo as buscas em cima do nosso modelo. Pensando assim, poderia fazer sentido manter as consultas junto s entidades (Entity). Vamos ver ento um recurso que nos fornece uma maneira diferente de organizar as consultas. Esse recurso chama-se Named Query. Vamos voltar ao cdigo que no utiliza o DAO, ou seja, inicializamos a query diretamente no mtodo main. S que dessa vez utilizaremos o mtodo
createNamedQuery

do EntityManager. Esse mtodo recebe o apelido da query,

alm do tipo do retorno. Repare que no colocamos mais o JPQL. Costumamos colocar a consulta em cima da entidade. Ento, nesse caso, como nossa consulta relacionada com as movimentaes de uma determinada conta, colocaremos ela na entidade Movimentacao.

Na classe Movimentacao usaremos a anotao @NamedQuery, que recebe dois atributos, o nome da query e o JPQL. esse nome que usamos no mtodo
createNamedQuery(..) do EntityManager. O segundo atributo o JPQL da

query. @NamedQuery(name = "mediaDaContaPeloTipoMovimentacao", query = "select avg(m.valor) from Movimentacao m where m.conta=:pConta and m.tipoMovimentacao = :pTipo") @Entity public class Movimentacao { // trecho omitido }

Agora podemos ver como praticamente no muda nada com o uso das Named Queries. A diferena que em vez de usarmos o mtodo createQuery, usamos o
createNamedQuery

e este, em vez de receber o JPQL, recebe somente seu

nome. Ao executar o cdigo continua funcionado: public class TestaConsultaFuncoes { public static void main(String[] args) { EntityManager manager = new JPAUtil().getEntityManager(); Conta conta = new Conta(); conta.setId(3); TypedQuery<Double> query = manager. createNamedQuery("mediaDaContaPeloTipoMovimentacao", Double.class); query.setParameter("pConta", conta); query.setParameter("pTipo", TipoMovimentacao.ENTRADA); Double media = query.getSingleResult(); System.out.println(media); } }

Uma vantagem que as Named Queries so verificadas quando o JPA carrega a entidade. Ento se fizermos mudanas no nosso modelo, mas esquecermos de alterar a query recebemos uma exceo antecipadamente.

Uma outra caracterstica imposta pelo uso das Named Queries o uso dos Named Parameter. No tem como concatenar a query na anotao, o que impe logo de incio a boa prtica.

Vimos ento duas formas de organizar as queries, usando o DAO e Named Queries. As duas at podem ser misturadas, usando Named Queries dentro DAO.