EasyCriteria – Utilizando a Criteria do JPA de um modo simples

Olá, tudo bem?

Vamos ver hoje sobre essa ferramenta que facilita o uso da Criteria do JPA que deixa o código bem limpo, fácil de utilizar e portável pelas implementações do JPA.

Ao final desse post se encontra disponível o código fonte para download.

O que é uma Criteria? É atualmente a melhor solução para consultas que são criadas de modo dinâmicos. Imagine uma tela onde se tem diversas opções de pesquisa. Pode ser por nome, por idade ou até mesmo pelos dois. Veja como ficaria a consulta caso fosse utilizado uma concatenação de String:

EntityManager em = emf.createEntityManager();
String hql = "select p from Person p where 1=1 ";

if(parameters[0].equals("name")){
	hql += " and p.name = '" + values[0] + "'";
}

if(parameters[1].equals("age")){
	hql += " and p.age = " + values[1];
}

TypedQuery<Person> query = em.createQuery(hql, Person.class);

System.out.println(query.getResultList());

Repare que no código acima é necessário fazer a concatenação de String o que pode levar ao famoso ataque hacker “SQL Injection”. A solução para evitar esse tipo de ataque seria utilizando query com parâmetros e não concatenação:

EntityManager em = emf.createEntityManager();
String hql = "select p from Person p where 1=1 ";

if(parameters.contains("name")){
	hql += " and p.name = :name";
}

if(parameters.contains("age")){
	hql += " and p.age = :age";
}

TypedQuery<Person> query = em.createQuery(hql, Person.class);

if(parameters.contains("name")){
	query.setParameter("name", values[0].toString());
}

if(parameters.contains("age")){
	query.setParameter("age", Integer.valueOf(values[1].toString()));
}

System.out.println(query.getResultList());

Note que o problema do SQL Injection foi resolvido mas agora o código deve verificar os parâmetros na hora de construir a query q na hora de popular os valores; o ato de popular os parâmetros acabam por gerar uma tarefa extra.

O pessoal da Java teve a brilhante ideia de criar o conceito de Criteria que é perfeita para esse tipo de utilização. Veja abaixo como ficaria o mesmo código utilizando a Criteria nativa do JPA:

EntityManager em = emf.createEntityManager();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Person> cq = cb.createQuery(Person.class);
Root<Person> root = cq.from(Person.class);
cq.select(root);

if(parameters.contains("name")){
	Path<String> name = root.get("name");
	cq.where(cb.and(cb.equal(name, values[0])));
}

if(parameters.contains("age")){
	Path<Integer> name = root.get("age");
	cq.where(cb.and(cb.equal(name, Integer.valueOf(values[1].toString()))));
}

TypedQuery<Person> query = em.createQuery(cq);

System.out.println(query.getResultList());

Veja como ficou prático a passagem de parâmetros já com seu valor. Não existe a necessidade de concatenar string, ou muito menos de ficar populando parâmetros depois que a query estiver pronta.

Infelizmente essa api do Criteria ficou muito complexa e demasiadamente verbosa. Para fazer apenas um “select p from Person p” a criteria abaixo seria necessária:

EntityManager em = emf.createEntityManager();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Person> cq = cb.createQuery(Person.class);
Root<Person> root = cq.from(Person.class);
cq.select(root);

TypedQuery<Person> query = em.createQuery(cq);
System.out.println(query.getResultList());

É muito código para uma função muito simples, listar todas as pessoas de uma tabela.

Para evitar toda essa verbosidade é que foi criado o projeto Open Source chamado EasyCriteria.

O EasyCriteria é simples, prático e tem uma abordagem de “esconder” toda a verbosidade facilitando o uso da Criteria do JPA. Utilizando o EasyCriteria a consulta acima ficaria assim:

EntityManager em = emf.createEntityManager();
EasyCriteria<Person> easyCriteria = EasyCriteriaFactory.createQueryCriteria(em, Person.class);

if(parameters.contains("name")){
	easyCriteria.whereEquals("name", values[0]);
}

if(parameters.contains("age")){
	easyCriteria.whereEquals("age", values[1]);
}

System.out.println(easyCriteria.getResultList());

Note que toda a verbosidade do JPA foi embora. Agora é possível ter um código simples para a construção de um query dinâmica. Sobre o código acima vale a pena destacar:

  • Linha 2: Uma instância do EasyCriteria é criada através de uma “factory”. Essa factory existe para abstrair todos os passos necessários para a criação de um objeto do tipo EasyCriteriaImp. Nas versões futuras virão outros tipos de EasyCriteria virão a exemplo do Tuple.

  • Linhas 5 e 9: A passagem de parâmetros ficou mais objetiva. Agora para se passar um parâmetro para comparar valores (“name = :name”) basta utilizar o método equals que espera como primeiro parâmetro o nome do atributo da classe; e como segundo o parâmetro o valor a ser comparado.

  • Linha 12: Para realizar a consulta não é mais necessário a utilização da interface Query. O próprio EasyCriteria toma conta dessa função. É possível extrair diretamente o resultado da query pelo EasyCriteria. Existem dois métodos que fazem isso atualmente o “EasyCriteria.getSingleResult()” e o “EasyCriteria.getResultList()”.

No site do EasyCriteria é possível encontrar diversos exemplos e os métodos que podem ser utilizados. Outra vantagem de se utilizar o EasyCriteria é que todos os seus comandos podem ser “linkados”:

easyCriteria.whereEquals("name", values[0]).whereEquals("age", values[1]).getResultList();

É uma bilioteca que está leve pois a única dependência é o JPA que o sistema já terá que ter configurado. Atenção: é necessário que sua aplicação já tenha o JPA rodando com alguma implementação.

Essa biblioteca foi desenvolvido utilizando JUnit e testado com Hibernate, OpenJPA e EclipseLink. O JUnit também utiliza do framework Cobertura para verificar se todo o código está sendo testado, atualmente 100% do código está coberto.

O EasyCriteria se encontra na versão beta e já tem em vista novas releases com novas funcionalidades.

Outra vantagem do EasyCriteria é que seu código não fica acoplado à alguma implementação. Hoje o Hibernate tem uma ferramenta de criteria muito boa e prática, mas você tem que ficar “preso” a ele. Com o EasyCriteria você poderá utilizar qualquer implementação. A prova disso é que os testes do EasyCriteria são realizados com as implementações citadas agora a pouco.

E EasyCriteria facilita funções como in, like, empty para a criteria do JPA. Além de realizar join (simples, sem adição de parâmetros), distinct e até order by tudo por criteria.

Aqui você encontra o EasyCriteria para download e para acesso a toda sua documentação (http://easycriteria.uaihebert.com).

Clique aqui para fazer o download do código fonte do post de hoje.

Espero que esse post/ferramenta possa te ajudar.

Qualquer dúvida/questionamento/opnião basta falar.

Até a próxima! o_

8 thoughts on “EasyCriteria – Utilizando a Criteria do JPA de um modo simples

      • Olá Hebert, muito bom o seu exemplo. Gostaria de sua aajuda para implemetar um criteria dinamico assim mas que além de dois campos que capturam a informação de um selectonmenu (tipo e empresa) eu também tenho que fazer um between num campo de data.
        A consulta é assim trago os resultados da tabela com data inicial e final no between, mas se o usuário selecionar uma opção diferente de “todos” no combo cliente eu filtro também este campo na consulta, o mesmo ocorre com o campo empresa.
        Mas se não for modificada a seleção padrão eu trago todos com o range de data e desprezo o tipo e empresa.
        Se puder ajudar fico muito grato.

        • Wagner, boa noite.

          Na página do projeto existe todo o exemplo que você precisa.

          Você precisa apenas fazer um if, caso o usuário selecionou a opção, faça isso se não faça a outra ação.

          Não tenho como descrever um código aqui pois não conheço seu modelo, mas na página o projeto descreve como utilizar passo a passo.

          Att,

Leave a Comment