consultas dinâmicas e typesafe em jpa 2

Upload: rg-angeliski

Post on 09-Jul-2015

345 views

Category:

Documents


0 download

TRANSCRIPT

Consultas dinmicas e typesafe em JPA 2.0

Pgina 1

Portugus (Brasil)

Conecte-se (ou Registrar)

Itens Tcnicos

Downloads e Trials

Comunidade

Consultas dinmicas e typesafe em JPA 2.0Como a Criteria API constri consultas dinmicas e reduz as falhas de tempo de execuo Pinaki Poddar, Senior Software Engineer, IBM Resumo: Uma consulta de objetos persistentes Java typesafe se um compilador puder verific-la quanto correo sinttica. A verso 2.0 da API de persistncia Java (JPA) introduz a Criteria API, que oferece o poder das consultas typesafe para aplicativos Java pela primeira vez e oferece um mecanismo para construir consultas de forma dinmica no tempo de execuo. Este artigo descreve como escrever consultas dinmicas, typesafe utilizando a Criteria API e a API de metamodelo diretamente associada. Data: 26/Out/2009 Nvel: Avanado Atividade: 3240 visualizaes Comentrios: 0 (Incluir comentrios) Mdia de classificao (baseada em 3 votos) A comunidade de desenvolvedores Java deu as boas-vindas JPA desde sua introduo em 2006. A prxima atualizao importante da especificao verso 2.0 (JSR 317) ser concluda no final de 2009 (consulte Recursos). Um dos principais recursos introduzidos na JPA 2.0 a Criteria API, que traz um recurso exclusivo para a linguagem Java: uma forma de desenvolver consultas que um compilador Java possa verificar quanto correo no momento da compilao. A Criteria API tambm inclui mecanismos para construir consultas de forma dinmica no tempo de execuo. Este artigo apresenta a Criteria API e o conceito de metamodelo diretamente associado. Voc saber como utilizar a Criteria API para desenvolver consultas que um compilador Java possa verificar quanto correo para reduzir erros de tempo de execuo, em contraste com as consultas baseadas em cadeias de caracteres da Java Persistence Query Language (JPQL). E atravs de consultas de exemplo que utilizam funes de banco de dados ou comparam uma instncia de modelo, demonstrarei o poder agregado da mecnica programtica de construo de consulta comparada com consultas JPQL que utilizam uma gramtica predefinida. O artigo supe que voc tenha familiaridade bsica da programao em linguagem Java e uso comum de JPA como EntityManagerFactory ou EntityManager . O que h de errado com a consulta JPQL? A JPA 1.0 introduziu a JPQL, uma linguagem poderosa de consulta considerada como a principal razo da popularidade da JPA. Entretanto, a JPQL sendo uma linguagem de consulta baseada em cadeia de caracteres com uma gramtica definida tem algumas limitaes. Para compreender uma das principais limitaes, considere o simples fragmento de cdigo na Listagem 1, que executa uma consulta JPQL para selecionar a lista de Pessoas maiores de 20 anos: Listagem 1. Uma consulta simples (e errada) JPQLEntityManager String jpql = Query query = List result = em = ...; "select p from Person where p.age > 20"; em.createQuery(jpql); query.getResultList();

Esse exemplo bsico mostra os seguintes aspectos principais do modelo de execuo de consulta na JPA 1.0: Uma consulta JPQL especificada como uma Cadeia de caractere (linha 2). O EntityManager a fbrica que constri uma instncia executvel de consulta dada uma cadeia de caractere JPQL (linha 3). O resultado da execuo de consulta consiste nos elementos de uma java.util.List no tipificado. Mas este exemplo simples tem um erro srio. Efetivamente, o cdigo compilar tranquilamente, mas falhar no tempo de execuo porque a cadeia de caractere de consulta JPQL est sintaticamente incorreta. A sintaxe correta para a segunda linha da Listagem 1 :String jpql = "select p from Person p where p.age > 20";

http://www.ibm.com/developerworks/br/java/library/j-typesafejpa/

31/08/2011 19:21:38

Consultas dinmicas e typesafe em JPA 2.0

Pgina 2

Infelizmente, o compilador Java no tem como detectar tal erro. O erro ser encontrado no tempo de execuo, na linha 3 ou na linha 4 (dependendo se o provedor de JPA analisa uma cadeia de caracteres JPQL de acordo com a gramtica JPQL durante a construo ou execuo da consulta). Como uma consulta typesafe ajuda? Uma das principais vantagens da Criteria API que ela probe a construo de consultas sintaticamente incorretas. A Listagem 2 reescreve a consulta JPQL da Listagem 1 utilizando a interface CriteriaQuery : Listagem 2. Etapas bsicas de escrita de uma CriteriaQueryEntityManager em = ... QueryBuilder qb = em.getQueryBuilder(); CriteriaQuery c = qb.createQuery(Person.class); Root p = c.from(Person.class); Predicate condition = qb.gt(p.get(Person_.age), 20); c.where(condition); TypedQuery q = em.createQuery(c); List result = q.getResultList();

Listagem 2 ilustra as estruturas principais da Criteria API e demonstra seu uso bsico: A linha 1 obtm uma instncia EntityManager atravs de um de vrios meios. Na linha 2, o EntityManager cria uma instncia do QueryBuilder. QueryBuilder o factory para o CriteriaQuery . Na linha 3, o factory QueryBuilder constri uma instncia do CriteriaQuery . Um CriteriaQuery genericamente tipificado. O argumento de tipo genrico declara o tipo de resultado que essa CriteriaQuery retornar na execuo. possvel fornecer vrios tipos de argumentos de tipo de resultado desde uma entidade persistente como a Person.class at uma de forma mais livre como a Object[] ao construir uma CriteriaQuery . Na linha 4, as expresses de consulta so definidas na instncia CriteriaQuery . As expresses de consulta so as unidades de ncleo ou ns montados em uma rvore para especificar uma CriteriaQuery . A figura 1 mostra a hierarquia das expresses de consulta definidas na Criteria API:

Figura 1. Hierarquia de interface das expresses de consulta

Para comear, a CriteriaQuery definida para consultar a partir de Person.class. Como resultado, uma instncia Root p retornada. Root uma expresso de consulta que denota a extenso de uma entidade persistente. Root essencialmente diz: "Avalie esta

http://www.ibm.com/developerworks/br/java/library/j-typesafejpa/

31/08/2011 19:21:38

Consultas dinmicas e typesafe em JPA 2.0

Pgina 3

consulta atravs de todas as instncias do tipo T." semelhante clusula FROM de uma consulta JPQL ou SQL. Observe tambm que Root tipificado genericamente. (Na verdade, toda expresso .) O argumento de tipo o tipo de valor que a expresso avalia. Portanto, Root denota uma expresso que avalia para Person.class. A linha 5 constri um Predicado . Um predicado outra forma comum de expresso de consulta que avalia quanto a verdadeiro ou falso. Um predicado construdo pelo QueryBuilder, que o factory no apenas para a CriteriaQuery , como tambm para as expresses de consulta. O QueryBuilder tem mtodos de API para construir todos os tipos de expresses de consulta suportados na gramtica tradicional JPQL, e mais alguns. Na listagem 2, o QueryBuilder usado para construir uma expresso que avalia se o valor de seu primeiro argumento de expresso numericamente maior que o valor do segundo argumento. A assinatura do mtodo :Predicate gt(Expression... selections);

O termo de projeo mais simples e frequentemente utilizado a classe candidata da consulta em si. Pode estar implcita, como demonstrado na Listagem 11. Listagem 11. CriteriaQuery seleciona a extenso do candidato por padroCriteriaQuery q = cb.createQuery(Account.class); Root account = q.from(Account.class); List accounts = em.createQuery(q).getResultList();

Na Listagem 11, a consulta de Account no especifica explicitamente esse termo de seleo e o mesmo que selecionar explicitamente a classe candidata. A listagem 12 apresenta uma consulta que utiliza um termo explcito de seleo: Listagem 12. CriteriaQuery com termo exclusivo explcito de seleoCriteriaQuery q = cb.createQuery(Account.class); Root account = q.from(Account.class); q.select(account); List accounts = em.createQuery(q).getResultList();

Quando o resultado projetado da consulta algo diferente da entidade persistente candidata em si, vrias outras construes so disponibilizadas para formar o resultado da consulta. Essas construes so disponibilizadas na interface QueryBuilder, como demonstrado na Listagem 13:

http://www.ibm.com/developerworks/br/java/library/j-typesafejpa/

31/08/2011 19:21:38

Consultas dinmicas e typesafe em JPA 2.0

Pgina 12

Listagem 13. Mtodos para formar o resultado da consulta CompoundSelection construct(Class result, Selection... terms); CompoundSelection array(Selection... terms); CompoundSelection tuple(Selection... terms);

Os mtodos na Listagem 13 constroem um termo de projeo composto de outras expresses selecionveis. O mtodo construct() cria uma instncia de determinado argumento de classe e invoca um construtor com valores dos termos de seleo de entrada. Por exemplo, se CustomerDetails uma entidade no-persistente tiver um construtor que toma argumentos String e int , uma CriteriaQuery pode ento retornar os CustomerDetails como resultado criando instncias a partir do nome e idade do Customer selecionado de uma entidade persistente de instncias, como demonstrado na Listagem 14: Listagem 14. Formando o resultado da consulta em instncias de uma classe por construct()CriteriaQuery q = cb.createQuery(CustomerDetails.class); Root c = q.from(Customer.class); q.select(cb.construct(CustomerDetails.class, c.get(Customer_.name), c.get(Customer_.age));

Os termos mltiplos de projeo tambm podem ser combinados em um termo composto que representa um Object[] ou Tuple. A listagem 15 mostra como compactar o resultado em um Object[]: Listagem 15. Formando o resultado da consulta em um Object[]CriteriaQuery q = cb.createQuery(Object[].class); Root c = q.from(Customer.class); q.select(cb.array(c.get(Customer_.name), c.get(Customer_.age)); List result = em.createQuery(q).getResultList();

Essa consulta retorna uma lista de resultados na qual cada elemento um Object[] de comprimento 2, o elemento de array zero o nome do Customer e o primeiro elemento a idade do Customer .Tuple uma interface definida por JPA para denotar uma linha de dados. Um Tuple conceitualmente uma lista de TupleElements em que TupleElement a unidade atmica e a raiz de todas as expresses de consulta. Os valores contidos em um Tuple podem ser acessados por um ndice de nmero inteiro baseado em 0 (semelhante ao resultado JDBC familiar), um nome alternativo do TupleElement ou diretamente pelo TupleElement. A listagem 16 mostra como compactar o resultado em um Tuple:

Listagem 16. Formando o resultado da consulta em TupleCriteriaQuery q = cb.createTupleQuery(); Root c = q.from(Customer.class); TupleElement tname = c.get(Customer_.name).alias("name"); q.select(cb.tuple(tname, c.get(Customer_.age).alias("age"); List result = em.createQuery(q).getResultList(); String name = result.get(0).get(name); String age = result.get(0).get(1);

Limitaes no aninhamentoTeoricamente possvel compor formas complexas de resultados aninhando termos como um Tuple cujos elementos so Object[]s ou Tuples. Entretanto, a especificao JPA 2.0 probe tal aninhamento. Os termos de entrada de um multiselect() no podem ser um array ou termo composto de valor tupla. Os nicos termos compostos permitidos como argumentos multiselect() so os criados pelo mtodo construct() (que essencialmente representa um elemento nico). Entretanto, o OpenJPA no coloca qualquer restrio no aninhamento de um termo de seleo composto dentro de outro. Essa consulta retorna uma lista de resultados onde cada um de seus elementos um Tuple. Cada tupla, por sua vez, carrega dois elementos acessveis pelo ndice ou pelo alias, se houver, dos TupleElements individuais ou diretamente pelo TupleElement. Dois pontos que merecem ateno na Listagem 16 so o uso de alias(), que uma forma de anexar um nome a qualquer expresso de consulta (criando uma nova cpia como efeito colateral) e um mtodo createTupleQuery() no QueryBuilder, que simplesmente uma alternativa ao createQuery(Tuple.class).

http://www.ibm.com/developerworks/br/java/library/j-typesafejpa/

31/08/2011 19:21:38

Consultas dinmicas e typesafe em JPA 2.0

Pgina 13

O comportamento desses mtodos individuais de formao de resultado e o que especificado como argumento de tipo de resultado da CriteriaQuery durante a construo combinado na semntica do mtodo multiselect(). Esse mtodo interpreta seus termos de entrada com base no tipo de resultado da CriteriaQuery para chegar forma do resultado. Para construir instncias CustomerDetails como na Listagem 14 usando multiselect(), necessrio especificar a CriteriaQuery a ser do tipo CustomerDetails e simplesmente invocar multiselect() com os termos que iro compor o construtor CustomerDetails , como demonstrado na Listagem 17: Listagem 17. multiselect() interpreta termos baseado no tipo de resultadoCriteriaQuery q = cb.createQuery(CustomerDetails.class); Root c = q.from(Customer.class); q.multiselect(c.get(Customer_.name), c.get(Customer_.age));

Como o tipo de resultado CustomerDetails , o multiselect() interpreta seus termos de projeo de argumento como o argumento construtor para o CustomerDetails . Se a consulta for especificada para retornar um Tuple, o mtodo multiselect() com os mesmos argumentos exatos que criariam instncias Tuple, como mostrado na Listagem 18: Listagem 18. Criando instncias Tuple com multiselect()CriteriaQuery q = cb.createTupleQuery(); Root c = q.from(Customer.class); q.multiselect(c.get(Customer_.name), c.get(Customer_.age));

O comportamento do multiselect() fica mais interessante com o Object como tipo de resultado ou se nenhum argumento de tipo for especificado. Em tais casos, se multiselect() for usado com um nico termo de entrada, o valor de retorno o termo selecionado. Mas se multiselect() contiver mais de um termo de entrada, o resultado um Object[]. Recursos avanados At aqui, enfatizei principalmente a natureza fortemente tipificada da Criteria API e o fato de que ela ajuda a minimizar os erros sintticos que causam lentido nas consultas JPQL baseadas em cadeia de caractere. A Criteria API tambm um mecanismo para construir consultas programaticamente e portanto normalmente referida como uma API dinmica de consulta. O poder de uma API de construo de consulta programvel limitado somente pela criatividade do seu usurio. Apresentarei quatro exemplos: Utilizao de uma verso fraca tipificada da API para construir consultas dinmicas Utilizao de uma funo suportada por banco de dados como expresso de consulta para estender a gramtica Edio de uma consulta para funcionalidade de busca dentro dos resultados Consulta por exemplo um padro familiar popularizado pela comunidade de banco de dados de objeto Tipificao fraca e construo dinmica de consulta A forte verificao de tipo da Criteria API baseada na disponibilidade das classes metamodelo instanciadas no momento do desenvolvimento. Entretanto, para alguns casos de uso, as entidades a serem selecionadas podem ser determinadas no tempo de execuo. Para suportar tal uso, os mtodos da Criteria API oferecem uma verso paralela na qual os atributos persistentes so referenciados por seus nomes (semelhante API de Reflexo Java) ao invs de por referncia aos atributos metamodelo estticos instanciados. Essa verso paralela da API pode suportar construo de consulta verdadeiramente dinmica sacrificando a verificao do tipo no tempo de compilao. A Listagem 19 reescreve um exemplo na Listagem 6 utilizando a verso tipificada fraca: Listagem 19. Consulta tipificada fracaClass cls = Class.forName("domain.Account"); Metamodel model = em.getMetamodel(); EntityType entity = model.entity(cls); CriteriaQuery c = cb.createQuery(cls); Root account = c.from(entity); Path balance = account.get("balance"); c.where(cb.and (cb.greaterThan(balance, 100), cb.lessThan(balance), 200)));

A API tipificada fraca, entretanto, no pode retornar expresses tipificadas genericamente, gerando um aviso de compilador para um modelo no verificado. Uma forma de livrar-se dessas mensagens de aviso incmodas usar uma instalao relativamente rara de elementos genricos Java: invocao de mtodo parametrizado, como demonstrado na invocao da Listagem 19 do mtodo get() para obter uma expresso de caminho.

http://www.ibm.com/developerworks/br/java/library/j-typesafejpa/

31/08/2011 19:21:38

Consultas dinmicas e typesafe em JPA 2.0 Expresses extensveis de armazenamento de dados

Pgina 14

Uma vantagem distinta de um mecanismo de construo dinmica de consulta que a gramtica extensvel. Por exemplo, possvel utilizar o mtodo function() na interface QueryBuilder para criar uma expresso suportada pelo banco de dados: Expression function(String name, Class type, Expression...args);

O mtodo function() cria uma expresso do nome dado e zero ou mais expresses de entrada. A expresso de function() avalia o tipo dado. Isso permite que um aplicativo crie uma consulta que avalie uma funo de banco de dados. Por exemplo, o banco de dados MySQL suporta uma funo CURRENT_USER() que retorna a combinao nome de usurio e nome de host como uma sequencia codificada UTF-8 para a conta MySQL que o servidor utilizou para autenticar o cliente atual. Um aplicativo pode utilizar a funo CURRENT_USER(), que no toma nenhum argumento, em uma CriteriaQuery , como demonstrado na Listagem 20: Listagem 20. Usando parmetros em uma CriteriaQueryCriteriaQuery q = cb.createTupleQuery(); Root c = q.from(Customer.class); Expression currentUser = cb.function("CURRENT_USER", String.class, (Expression[])null); q.multiselect(currentUser, c.get(Customer_.balanceOwed));

Observe que uma consulta equivalente simplesmente no possvel para expressar em JPQL, pois tem uma gramtica definida com um nmero fixo de expresses suportadas. Uma API dinmica no estritamente limitada por um conjunto fixo de expresses. Consulta editvel Uma CriteriaQuery pode ser editado programaticamente. As clusulas da consulta como seus termos de seleo, predicado de seleo em uma clusula WHERE e termos de pedido em uma clusula ORDER BY podem todos ser modificados. Esse recurso de edio pode ser usado em uma instalao do tipo "procura em resultado", por onde um predicado de consulta adicionalmente refinado em etapas sucessivas adicionando mais restries. O exemplo na Listagem 21 cria uma consulta que solicita seu resultado por nome e ento edita a consulta para classificar tambm pelo CEP: Listagem 21. Editando uma CriteriaQueryCriteriaQuery c = cb.createQuery(Person.class); Root p = c.from(Person.class); c.orderBy(cb.asc(p.get(Person_.name))); List result = em.createQuery(c).getResultList(); // start editing List orders = c.getOrderList(); List newOrders = new ArrayList(orders); newOrders.add(cb.desc(p.get(Person_.zipcode))); c.orderBy(newOrders); List result2 = em.createQuery(c).getResultList();

Avaliao na memria em OpenJPACom os recursos estendidos do OpenJPA, o exemplo de procura nos resultados da Listagem 21 pode se tornar ainda mais eficiente avaliando a consulta editada na memria. Esse exemplo impe que o resultado da consulta editada seja um subconjunto restrito do resultado original. Como o OpenJPA pode avaliar uma consulta na memria quando uma coleo candidata especificada, a nica modificao necessria que a ltima linha da Listagem 21 fornea o resultado da consulta original:List result2 = em.createQuery(c).setCandidateCollection(result).getResultList();

Os mtodos setter na CriteriaQuery select(), where() ou orderBy() apagam os valores anteriores e os substituem com novos argumentos. A lista retornada pelos mtodos getter correspondentes, tais como getOrderList() no est ativa isto , adicionar ou remover elementos na lista retornada no modifica o CriteriaQuery; alm disso, alguns fornecedores podem at retornar uma lista imutvel de proibir uso inadvertido. Portanto, uma boa prtica copiar a lista retornada em uma nova lista antes de adicionar ou remover novas expresses. Consulta por exemplo

http://www.ibm.com/developerworks/br/java/library/j-typesafejpa/

31/08/2011 19:21:38

Consultas dinmicas e typesafe em JPA 2.0

Pgina 15

Outra facilidade til em uma API de consulta dinmica que ela pode suportar consulta por exemplo com relativa facilidade. A consulta por exemplo (desenvolvida pela IBM Research em 1970) normalmente citada como um exemplo primrio da usabilidade de software pelo usurio final. A ideia de consulta por exemplo que ao invs de especificar os predicados exatos para uma consulta, uma instncia de modelo apresentada. Dada a instncia de modelo, uma conjuno de predicados onde cada um deles uma comparao para um valor de atributo noanulvel, no-padro da instncia de modelo criada. A execuo dessa consulta avalia o predicado para encontrar todas as instncias que correspondem instncia de modelo. A consulta por exemplo foi considerada para incluso na especificao JPA 2.0 no est includa. OpenJPA suporta esse estilo de consulta atravs de sua interface estendida OpenJPAQueryBuilder , como demonstrado na Listagem 22 : Listagem 22. Consulta por exemplo utilizando extenso do OpenJPA da CriteriaQueryCriteriaQuery q = cb.createQuery(Employee.class); Employee example = new Employee(); example.setSalary(10000); example.setRating(1); q.where(cb.qbe(q.from(Employee.class), example);

Como mostra esse exemplo, a extenso do OpenJPA da interface QueryBuilder suporta a seguinte expresso:public Predicate qbe(From