Como organizar consultas JPQL: Named Queries ou Queries Dinâmicas?
Conheça as principais vantagens e desvantagens de cada abordagem ao escrever consultas JPQL na sua aplicação
Consultar dados é uma parte fundamental da persistência. Poder persistir dados não é de muita ajuda se não temos uma forma eficiente de recuperá-los. Por este motivo, a JPA nos fornece várias maneiras de recuperar objetos persistidos, que vai desde a forma programática permitindo obter uma entidade via ID, até o uso sofisticado da API de Criteria e da linguagem de consulta JPQL. O melhor de tudo, é que todas as maneiras garantem a portabilidade entre bancos de dados.
A Java Persistence Query Language (JPQL) é a maneira mais conhecida e utilizada de consultar objetos na JPA pelos desenvolvedores, isso se deve por ela ter sido fortemente inspirada na sintaxe do SQL. Apesar disso, o foco dela não está relacionado a tabelas e colunas, mas sim a objetos e seus atributos.
O problema de ter uma linguagem de consulta que é utilizada dentro de outra linguagem (Java) é decidir onde armazenar estas consultas. Por padrão, a JPA nos permite definir as consultas JPQL de 2 formas distintas:
- consultas estáticas através de Named Queries;
- e/ou consultas dinâmicas, normalmente criadas dentro de classes DAO;
Decidir qual utilizar influencia diretamente na organização do código da aplicação, que por sua vez impacta na comunicação da equipe. Não é por acaso que é muito comum a discussão na comunidade sobre qual a melhor estratégia para organizar as consultas JPQL na aplicação. Alguns defendem o uso de named queries, enquanto outros não abrem mão da flexibilidade de queries dinâmicas.
Named Queries
Uma Named Query nada mais é do que uma consulta JPQL estaticamente definida com seus parâmetros já pré-definidos. Todas as consultas desse tipo são declaradas a nível de classes e centralizadas dentro das entidades através da anotação @NamedQuery.
Por exemplo, para buscar um ou mais usuários com um determinado email, poderíamos ter uma consulta declarada desta maneira:
@NamedQuery(name="Usuario.buscaPorEmail",
query="select u from Usuario u where u.email = :email")
@Entity
public class Usuario {
// ...
}
O atributo name
da @NamedQuery
define o nome da nossa consulta e será utilizado posteriormente ao invocarmos a consulta, enquanto o atributo query
define a nossa consulta JPQL em si.
Para executarmos a named query acima nos nossos DAOs, devemos invocar o método createNamedQuery
da EntityManager
passando o nome da named query, como no código abaixo:
public Usuario buscaUsuarioPorEmail(String email) {
return this.manager
.createNamedQuery("Usuario.buscaPorEmail", Usuario.class)
.setParameter("email", email)
.getSingleResult();
}
Por uma named query ser uma JPQL estaticamente definida, seus parâmetros também o são. O que nos impõe uma boa prática, a prática de nunca concatenar os parâmetros à consulta, mas sempre setar os parâmetros na própria query.
Outra característica interessante das named queries, é que elas são parseadas e compiladas para SQL e cacheadas em memória durante a incialização da EntityManagerFactory
, o que pode trazer ganhos de performance em relação a JPQL dinâmicas, na qual são parseadas sempre que executadas.
Queries Dinâmicas
Queries dinâmicas, como o nome já diz, são consultas JPQL que podem ser definidas e executadas em qualquer classe em tempo de execução. Normalmente elas são organizadas na camada de persistência, dentro de DAOs ou Repositories. A mesma consulta acima poderia ser rescrita da seguinte maneira:
public Usuario buscaUsuarioPorEmail(String email) {
String jpql = "select u from Usuario u where u.email = :email";
return this.manager
.createQuery(jpql, Usuario.class)
.setParameter("email", email)
.getSingleResult();
}
A diferença na execução de uma query dinâmica em relação a named queries é bem sútil. Basicamente criamos a consulta dentro do método do DAO e logo em seguida a executamos através do método createQuery
da EntityManager
.
Essa mesma consulta poderia ser obtida de um arquivo XML ou properties sem muita dificuldade, o que poderia ajudar na organização e manutenção. Apesar dela também poder ser construída de acordo com os filtros preenchidos pelo usuário, sabemos que isso é melhor resolvido com a API de Criteria.
De certa forma, ter as consultas dentro das classes DAO parece ser mais lógico, já que estas classes representam nossa camada de persistência e tentam abstrair todos os detalhes do nosso framework de persistência.
Qual a melhor?
O fato é que named queries são realmente mais rápidas por serem parseadas para SQL e cacheadas apenas uma única vez, enquanto consultas dinâmicas são parseadas sempre que executadas. Alguns providers até conseguem cachear as queries dinâmicas, mas não são todos nem todas as queries.
No entanto, o uso de JPQL dinâmicas nos traz uma maior flexibilidade já que elas podem ser criadas em tempo de execução em qualquer lugar na aplicação. Além disso, não poluímos nossas entidades com várias named queries. Em contrapartida, erros de sintaxe na JPQL são encontrados mais facilmente com named queries, já que elas são validadas na inicialização da aplicação.
Outra vantagem de queries dinâmicas bastante negligenciada é a possibilidade do uso do hot deploy da IDE. Por ela ser definida em tempo de execução, podemos alterá-la sem ter que reiniciar o servidor Web.
No geral, a diferença de performance entre as duas abordagens não será gritante se você escrever suas queries sem concatenação de parâmetros, souber aproveitar os índices e joins do banco de dados e o configurar corretamente cache de segundo nível. Vale lembrar que se você tem certeza que sua query não mudará e ela é bastante executada dentro da aplicação, então é possível considerá-la uma forte candidata a named query.
Enfim, centralizar as consultas dentro das entidades ou dentro dos DAOs é uma decisão que sempre leva a discussões calorosas entre os membros da equipe. O importante é entender os prós e contras de cada estratégia antes de bater o martelo.
Se você quiser saber mais sobre consultas na JPA e estiver disposto a boas discussões em sala de aula, conheça nosso curso e solidifique seus conhecimentos!
E você, prefere organizar suas consultas JPQL através de named queries ou queries dinâmicas dentro dos DAOs?
Desenvolvedor e instrutor na TriadWorks