Mapeamento Objeto-Relacional em Java usando Oracle Toplink: Parte 3

abr 17 2009

Usando a JPQL (Java Persistence Query Language)

A JPQL também é uma especificação da JPA. É uma linguagem, semelhante à SQL. Diferente da SQL que trabalha com tabelas, a JPQL trabalha com entidades (classes).

Selecionando todos os objetos de uma entidade
As queries são criadas e executadas com o auxílio da classe EntityManager, usando o método createQuery. Existem outros dois métodos semelhantes: createNativeQuery e createNamedQuery. O primeiro é para usar queries escritas em SQL puro (nativas), o segundo veremos mais adiante.

Crie uma classe chamada SelectAll, o código dela fica assim: http://pastebin.com/f3aa9d3bc. Esteja atento a duas coisas:

  • Não tem SELECT *;
  • Você deve importar a interface Query do pacote javax.persistence.

O código mostrado é muito simples. Vamos agora começar a filtrar resultados e ver como os joins não são tão complicados aqui :)

Filtrando resultados
Vamos fazer apenas uma filtragem simples. Note que a JPQL é muito semelhante à SQL. Vamos selecionar todos os status que começam com a letra V. Você pode pensar: “em SQL eu uso o like, o que será que eu uso na JPQL?”. O like também! Veja o código:

Query query = gerenciador.createQuery("SELECT s FROM Status s WHERE s.nome LIKE 'V%'");
List<Status> status = query.getResultList();
for(Status s : status){
    System.out.println(s.getNome());
}

Nada demais, quase igual à SQL. O nome dos atributos deve ser equivalente ao nome dado nas classes. Esqueça as tabelas no banco de dados, você não vai usar nada de lá :) Não explicitamente.

JOIN’s
Uma das coisas mais legais em trabalhar com banco de dados relacionais é fazer JOIN. O problema é que os joins costumam gerar consultar enormes. Vamos fazer a seguinte consulta: Eu quero obter todos os clientes que compraram um produto cujo nome (do produto, caso haja ambiguidade aqui) começa com a letra A. :) Como a JPQL é muito semelhante à SQL, vamos fazer assim ué:

SELECT p FROM PessoaFisica p, Compra c, ItemDeCompra ic, Produto p
WHERE ic.compra = c AND c.cliente = p AND ic.produto = p AND p.nome LIKE 'A%'

Taí, um SELECT pra ninguém botar defeito. Na verdade, eu boto defeito. A JPQL não seria tão útil se não pudesse melhorar a forma de fazer consultas no banco de dados. É nesse momento que as pessoas têm mais problemas com SQL :) Vamos reescrever a query?

SELECT ic.compra.pessoa FROM ItemDeCompra ic WHERE ic.produto.nome LIKE 'A%'

Melhorou? Pois é, o Toplink processa essa query e gera algo semelhante à query anterior. Mas para o programador é mais fácil não usar tantos joins. Nós declaramos estar selecionando dados somente de uma entidade, mas por baixo dos panos é feita uma consulta que acessa quatro tabelas. Isso sim é VHPL :)

Bind variable
Já aprendemos a usar as queries. Mas uma coisa muito importante que encontramos no JDBC e ainda não vimos na JPA é a possibilidade de fornecer parâmetros às queries.

Diferente de como acontece no JDBC, na JPA os parâmetros possuem nome e não número. Em JDBC declaramos os parâmetros usando pontos de exclamação (“?”) e configuramos estes parâmetros usando seu número. Na JPA, os parâmetros têm nome:

SELECT s FROM STATUS s WHERE s.nome = :nome

O nome do parâmetro é “nome”. Todo parâmetro é identificado pelos dois pontos (“:”) seguidos do nome do parâmetro. Vamos agora ver um código onde setamos o parâmetro:

Query query = gerenciador.createQuery("SELECT s FROM Status s WHERE s.nome = :nome");
query.setParameter("nome", "Vivo");
List<Status> status = query.getResultList();

for(Status s : status) {
     System.out.println(s.getNome());
}

Obs: Se você quiser, pode configurar todos os parâmetros usando números também.

Dando nome às queries
Bom, já vimos como criar queries, já vimos como filtrar resultados, como fazer join’s e como adicionar parâmetros a uma query. Um recurso interessante é deixar a query pronta, e depois chamar ela pelo nome. :)

Você cria uma NamedQuery na entidade e depois acessa ela pelo nome. Legal né?! Vamos ver como fazer isso. Abra a classe Status e adicione abaixo da linha @Entity as seguintes linhas:

@NamedQueries({
    @NamedQuery(name = "SelecionarTodosStatus",
                query = "SELECT s FROM Status s"),

    @NamedQuery(name = "SelecionarStatusPeloNome",
                query = "SELECT s FROM Status s WHERE s.nome = :nome")
})

Entendeu o que fizemos? Criamos uma query chamada SelecionarTodosStatus e outra criada SelecionarStatusPeloNome. Agora vamos usá-la. Crie a classe SelectNamedQuery, com o seguinte código: http://pastebin.com/f65f464b8.

Ao invés de usarmos o createQuery, usamos o createNamedQuery. Passamos o nome da Query, o Toplink encontra ela, processamos os parâmetros (se existirem) e pronto, temos o resultado! :)

Na próxima, e última, parte vou apresentar uma ideia nova para criação de classes Dao, usando o recurso de tipagem genérica do Java. Não é algo ligado ao Toplink ou à JPA. Vou mostrar o modelo de criação das classes Dao, e exemplificar como eu faria usando Toplink (mostrando como é fácil) e JDBC (vai dar mais trabalho).

5 responses so far

  1. Caro Francisco.
    Por um acaso não está faltando um import para a classe Status?

    Digo isso por causa da linha 17:
    List<-S-t-a-t-u-s> todos = queryTodos.getResultList();

    Aqui pediu um import, mas mesmo importanto não rodou…

    O erro retornado foi o seguinte:

    Exception in thread "main" java.lang.IllegalArgumentException: NamedQuery of name: SelecionarTodosStatus not found.
    at oracle.toplink.essentials.internal.ejb.cmp3.base.EJBQueryImpl.getDatabaseQuery(EJBQueryImpl.java:422)
    at oracle.toplink.essentials.internal.ejb.cmp3.base.EJBQueryImpl.setAsSQLReadQuery(EJBQueryImpl.java:136)
    at oracle.toplink.essentials.internal.ejb.cmp3.base.EJBQueryImpl.getResultList(EJBQueryImpl.java:464)
    at db.SelectNamedQuery.main(SelectNamedQuery.java:18)
    Java Result: 1

    Estou me referindo a classe que vc. disponibilizou no endereço:
    http://pastebin.com/f65f464b8

    por sinal achei estranho o pacote aonde a classe está.

    Um grande abraço e até mais!!!
    ps.
    escrevi -S-t-a-t-u-s pq o esta é uma palavra reservada do sistema de comentários!

  2. Francisco Souza

    Salve Leonam! :D
    Basta adicionar o import mesmo, mas tem que ficar esperto pra importar a classe correta, por que na API do Java existem algumas classes que se chamam Status.

    Mas o erro que você me passou indica que o EntityManager não ta encontrando a query "SelecionarTodosStatus". Você adicionou ela na classe Status? Sua classe Status está assim: http://is.gd/1QsmT?

    Abraços! ;)

  3. Olá Francisco!
    Obrigado pelo seu breve retorno, o meu problema foi solucionado, realmente o EntityManager não estava encontrando.
    De toda forma, hoje usei aquela classe que estava naquele link, dei um build e tudo funcionou corretamente.
    A classe estava no EntityManager mas de alguma forma realmente não estava encontrando, dei um re-build e funcionou!

    Obrigado!!!

  4. Tudo Bem Francisco! D+ seu exclarecimento…achei muito bom e é dificil achar um material tão completo sobre o toplink com jpa e query como você mostrou…

    Eu estou tendo um problema que talvez com sua experiencia você já deve ter passado:
    Eu tenho um relacionamento muitos para zero, que é significa muitos para um ou zero, mas quando efetuo o cadastro com o valor null na chave estrangeira e tento fazer um select do registro ele não me traz o registro que tem o campo null.
    preciso muito resolver isso…
    obrigado.
    Meu e-mail é adalton@dri.cefetmg.br

  5. [...] Parte 3: Usando a JPQL (Java Persistence Query Language); [...]

Leave a Reply

Spam protection by WP Captcha-Free