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

Consultas com o Java Persistence Query Language

Por padro, todo banco de dados relacional aceita a Structured Query Language (SQL) para realizar consultas e manipulaes em modelos relacionais. Quando adotamos o JPA, uma das coisas que buscamos nos distanciar o mximo possvel do modelo relacional e focar exclusivamente no modelo orientado a objetos e seus estados:

Alm disso, o SQL nos obriga a conhecer bem o modelo de dados, e podemos facilmente notar isso analisando um simples cdigo de consulta que usvamos quando trabalhvamos diretamente com a API do JDBC.

Repare que no cdigo abaixo conhece o nome da tabela e das colunas do modelo relacional:

public List<Conta> consultaContasPorTitularPaginada (String titular, int posicaoInicial, int qntRegistros) { String sql = "SELECT * FROM conta WHERE titular LIKE ? LIMIT ?,?"; try { PreparedStatement stmt = conexao.prepareStatement(sql); stmt.setString(1, "%"+titular+"%"); stmt.setInteger(2, posicaoInicial); stmt.setInteger(3, qntRegistros) ResultSet rs = stmt.executeQuery(); List<Conta> contas = new ArrayList<Conta>(); while (rs.next()) { Conta conta = new Conta(); conta.setId(Long.valueOf(rs.getString("id"))); conta.setAgencia(rs.getString("agencia")); conta.setBanco(rs.getString("banco")); conta.setNumero(rs.getString("numero")); conta.setTitular(rs.getString("titular")); contas.add(conta); } rs.close(); stmt.close(); } catch (SQLException ex) { System.out.println("Erro na consulta da conta."); } return contas; }

Alm de termos que escrever uma String com o SQL, que ser enviada ao banco, precisamos saber o nome da tabela e de suas colunas. E mais, ao recebermos a resposta da consulta ao banco, via ResultSet, precisamos ainda passar essas informaes para um objeto e retorn-lo posteriormente. Todo o mapeamento entre o mundo relacional e OO feito manualmente.

Pesquisas orientadas a objeto


Com JPA podemos tornar isso mais simples usando apenas o Java Persistence Query Language. O JPQL uma linguagem de consulta, assim como o SQL, porm orientada a objetos. Isso significa que quando estivermos pesquisando dados, no consideramos nomes de tabelas ou colunas, e sim entidades e seus atributos. Atravs dessa linguagem temos acesso a recursos que o SQL no nos oferece, como polimorfismo e at mesmo maneiras mais simples de buscarmos informaes atravs de relacionamentos.

Criando e executando Queries


Vamos criar uma classe chamada TestaConsulta no pacote br.com.caelum.financas.teste com um mtodo main:
package br.com.caelum.financas.teste; public class TestaConsulta { public static void main(String[] args) { } }

Faremos primeiro uma consulta simples que retornar todas as movimentaes de uma determinada conta. Como j sabemos vamos criar o EntityManager atravs da classe auxiliar JPAUtil. Alm disso, vamos instanciar uma conta que possui a id 2 (essa id deve existir no banco):

EntityManager manager = new JPAUtil().getEntityManager(); Conta conta = new Conta(); conta.setId(2);

Depois disso vamos usar o mtodo createQuery(..) do EntityManager passando para ele um cdigo JPQL. Utilizamos a clausula select-from parecida com SQL mas aqui o alvo da consulta no a tabela no banco mas sim a classeMovimentacao do nosso modelo OO. Adicionamos o alias m para facilitar a digitao:

Query query = manager.createQuery("select m from Movimentacao m");

A clausula where filtar a consulta. Repare que filtramos por m.conta.id, isto pela id da conta da movimentao. Por fim concatenamos a id da conta o JPQL:

Query query = manager.createQuery("select m from Movimentacao m where m.conta.id=" + conta.getId());

retorno

do

mtodo createQuery(..) ser

um

objeto

do

tipo javax.persistence.Query. Atravs desse objeto, podemos solicitar que o resultado seja uma lista de objetos, em nosso caso, de movimentaes, atravs do mtodogetResultList(). Depois s iterar na lista.
package br.com.caelum.financas.teste; // imports omitidos public class TestaConsulta { public static void main(String[] args) { EntityManager manager = new JPAUtil().getEntityManager(); Conta conta = new Conta(); conta.setId(2); Query query = manager.createQuery("select m from Movimentacao m where m.conta.id=" + conta.getId()); List<Movimentacao> movimentacoes = query.getResultList(); for (Movimentacao m : movimentacoes) { System.out.println("\nDescricao ..: " + m.getDescricao()); System.out.println("Valor ......: R$ " + m.getValor()); } }

Recapitulando,

criamos

um

objeto

do

tipo Query atravs

do

mtodo createQuery(..) do EntityManager que recebeu como parmetro um JPQL. Apesar de lembrar um SQL a consulta sobre nossas classes do modelo, classes que podemos que podemos nos referir atravs de um alias. Alm disso, filtramos a consulta pela id utilizando o operador "." at chegar no atributo id da conta da movimentao. Por fim, concatenamos a id procurado na consulta. Com a query pronta obtivermos o resultado, uma lista de movimentaes, chamando o

mtodo getResultList() do objeto query criado. Com a lista podemos precorre-la de maneira tradicional atravs do um foreach.

Problemas ao usar JPQL


Perceba que escrevemos uma consulta bem parecida com SQL, mas em nenhum momento nos preocupamos com qual tabela estamos trabalhando, muito menos com nomes de colunas. Nossa consulta toda baseada no que sabemos dos nossos objetos. Por exemplo, sabemos que a Movimentacao tem um atributo que chama conta e este, por sua vez, um atributo que se chama id, com isso podemos caminhar por eles. Mas tem ainda um problema aqui. que a consulta ainda est com um gosto de SQL, estamos comparando o id. Seria mais interessante se pudssemos passar para o JPQL o nosso objeto do tipo Conta e ele fosse responsvel por descobrir, que nesse caso, o relacionamento estava se dando pelo atributo id daquela conta. Alm do mais, sabemos que ficar concatenando queries pode nos levar a problemas grandes, como SQL Injection.

Trabalhando com parmetros na consulta JPQL de maneira segura


Para resolver ambos os problemas, o JPQL tem uma maneira de definir parmetros bem similar ao PreparedStatement. Na nossa consulta, queremos definir um parmetro na query para indicar que no lugar dele vai entrar a conta recebida de alguma forma. No nosso cdigo JPQL vamos remover a concatenao e definir um parmetro indicado pelo ?1. Passaremos este parmetro atravs do

mtodo setParameter(..) do objeto query que recebe a posio do parmetro e valor que ser recibido. Essa forma conhecido como Positional Parameter Notation. Abaixo temos um exemplo do uso:
package br.com.caelum.financas.teste;

// imports omitidos

public class TestaConsulta { public static void main(String[] args) { EntityManager manager = new JPAUtil().getEntityManager(); Conta conta = new Conta(); conta.setId(1); Query query = manager .createQuery("select m from Movimentacao m where m.conta=?1"); query.setParameter(1, conta); List<Movimentacao> movimentacoes = query.getResultList(); for (Movimentacao m : movimentacoes) { System.out.println("\nDescricao ..: " + m.getDescricao()); System.out.println("Valor ......: R$ " + m.getValor()); } } }

O problema da Positional Parameter Notation que quando temos muitos parmetros fica fcil confundir-se com os nmeros, com isso acabamos errando as posies.

Usando Named Parameters


A segunda maneira que podemos fazer ir dando nomes aos parmetros da query. Essa segunda maneira conhecida como Named Parameter Notation. Com ela, fica mais fcil identificar os parmetros, diminuindo a probabilidade de se cometer algum erro. Agora o parmetro tem um nome, no nosso caso :pConta e no mtodo setParameter(..) usamos este parmetro no lugar da posio deixando mais claro qual parmetro est sendo passado. Desse jeito, teramos o seguinte cdigo:
package br.com.caelum.financas.teste; // imports omitidos public class TestaConsulta { public static void main(String[] args) { EntityManager manager = new JPAUtil().getEntityManager(); Conta conta = new Conta(); conta.setId(1); Query query = manager .createQuery("select m from Movimentacao m where m.conta=:pConta"); query.setParameter("pConta", conta);

List<Movimentacao> movimentacoes = query.getResultList(); for (Movimentacao m : movimentacoes) { System.out.println("\nDescricao ..: " + m.getDescricao()); System.out.println("Valor ......: R$ " + m.getValor()); } } }

Ordenando o resultado da pesquisa


Podemos tambm estabelecer um critrio para manter nossa lista organizada. Vamos ento ordenar a listagem pelo valor decrescente da movimentao. Fazemos isso usando a clausula order by com o valor da movimentao, accessando o alias m.valor:

Query query = manager .createQuery("select m from Movimentacao m where m.conta = :pConta " + " order by m.valor desc");

query.setParameter("pConta", conta);

Melhorando nosso filtro


Agora queremos tambm filtrar a nossa listagem de movimentaes pelo tipo da movimentao, podendo ser ele de ENTRADA ou SAIDA. Para isso, basta adicionar mais um filtro na clusula where com o conectivo AND que levar em considerao o tipo da movimentao definido pelo m.tipoMovimentacao. Isto o atributo tipoMovimentacao da classeMovimentacao. O cdigo ficar assim para a consulta das movimentaes de SAIDA:
Query query = manager .createQuery("select m from Movimentacao m where m.conta = :pConta" + " and m.tipoMovimentacao = :pTipo" + " order by m.valor desc"); query.setParameter("pConta", conta); query.setParameter("pTipo", TipoMovimentacao.SAIDA);

Resumo
Nesta aula vimos como consultar o nosso modelo atravs de JPQL. Tambm foi apresentado a possibilidade de passar parmetros para essas consultas sendo o mais recomendado o Named Parameter Notation que utilize o nome do parmetro no lugar da posio do parmetro na query. Tudo atravs do objeto do tipo javax.persistence.Query que abre peloEntityManager. Este mesmo objeto retornou os dados da consulta atravs do mtodo getResultList().

Вам также может понравиться