Olá Pessoal, tudo bom?
Esse artigo está relacionado ao artigo de organização de método de busca com SQL e organização de método de busca com HQL mas agora focado em Criteria, com classes relacionadas ao Hibernate.
Definição de Filtro
Um filtro é uma estrutura que nos permite inserir uma ou mais informações sobre um determinado assunto e realizar uma pesquisa deste. O exemplo mais clássico de um filtro é o Google, a Figura 01 exibe uma animação de uma busca utilizando o Google:
No caso de uma busca do Google, existe apenas 1 filtro. Mas nem todos os sistemas conseguem estabelecer telas que possuam apenas um campo de filtro, por exemplo a tela exibida na Figura 02, mostra uma área com vários campos que podem ser utilizados para buscas:
Repare que existem diversos filtros como ID, que representa a identificação da Categoria, o Name, que representa o nome da categoria, Created User, que criou a categoria e por fim a Created Date, que representa a data de criação no banco de dados daquela categoria. Nessa tela você pode pesquisar não inserindo nenhum campo, todos os campos ou uma mescla destas opções.
Exemplo de método de busca por filtros – padrão Criteria/Hibernate
Uma das formas mais simples de criar métodos de busca é inserir todos os campos de um filtro como parâmetros desse método. Vamos supor a seguinte classe Endereco, conforme mostra a Figura 03:
O código desta classe está representado abaixo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
public class Endereco { private Long id; private String rua; private Integer numero; private String complemento; private String bairro; private String cidade; private String estado; private String pais; public Long getId() { return this.id; } public void setId(Long id) { this.id = id; } public String getRua() { return this.rua; } public void setRua(String rua) { this.rua = rua; } public Integer getNumero() { return this.numero; } public void setNumero(Integer numero) { this.numero = numero; } public String getComplemento() { return this.complemento; } public void setComplemento(String complemento) { this.complemento = complemento; } public String getBairro() { return this.bairro; } public void setBairro(String bairro) { this.bairro = bairro; } public String getCidade() { return this.cidade; } public void setCidade(String cidade) { this.cidade = cidade; } public String getEstado() { return this.estado; } public void setEstado(String estado) { this.estado = estado; } public String getPais() { return this.pais; } public void setPais(String pais) { this.pais = pais; } } |
Vamos definir um método de busca, no padrão Criteria, para todos os atributos deste método:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
//Imports necessarios..... import java.util.Collection; import org.apache.commons.lang3.StringUtils; import org.hibernate.Criteria; import org.hibernate.Session; import org.hibernate.criterion.MatchMode; import org.hibernate.criterion.Restrictions; //..... definicao da classe e outras coisas @SuppressWarnings("unchecked") public Collection<Endereco> findByFilter(String rua, Integer numero, String complemento, String bairro, String cidade, String estado, String pais) { Session session = HibernateUtil.getSession(); try { Criteria c = session.createCriteria(this.entityClassName, "endereco"); if (StringUtils.isNotBlank(bairro)) { c.add(Restrictions.like("bairro", bairro, MatchMode.ANYWHERE)); } if (StringUtils.isNotBlank(cidade)) { c.add(Restrictions.like("cidade", cidade, MatchMode.ANYWHERE)); } if (StringUtils.isNotBlank(complemento)) { c.add(Restrictions.like("complemento", complemento, MatchMode.ANYWHERE)); } if (StringUtils.isNotBlank(estado)) { c.add(Restrictions.like("estado", estado, MatchMode.ANYWHERE)); } if (StringUtils.isNotBlank(pais)) { c.add(Restrictions.like("pais", pais, MatchMode.ANYWHERE)); } if (StringUtils.isNotBlank(rua)) { c.add(Restrictions.like("rua", rua, MatchMode.ANYWHERE)); } if (numero != null) { c.add(Restrictions.eq("numero", numero)); } return c.list(); } catch (Exception e) { e.printStackTrace(); return null; } finally { session.close(); } } |
Esse método possui mais muitos parâmetros, tornando a sua modificação muito mais complexa. Será que é possível organizá-lo de uma maneira a tornar mais fácil suas modificações, pelo menos no aspecto de modificação de parâmetros? Sim é possível!
Campos de um Filtro x Parâmetros de um método
No caso do exemplo acima existem ao todo 7 parâmetros que representam o filtro de busca de uma tela de pesquisa de endereços. Assim, todos os campos que estão presentes no filtro são adicionados como parâmetros do método.
Isso é uma boa prática? Caso seja necessário adicionar mais campos no filtro de busca será necessário mexer nos parâmetros do método? Existe algum problema relacionado a isso?
Respondendo a primeira pergunta, isso não é uma boa prática, pois se a tela de pesquisa conter mais de 10, 15 campos, será necessário você adicionar 10, 15 parâmetros no seu método. Além disso, pode ser que esse método de busca seja utilizado em outras partes do sistema (ou talvez outros sistemas!), assim realizar uma mudança em sua assinatura, adicionando novos parâmetros, pode exigir mudanças de outros desenvolvedores no código, o que ocasiona retrabalho e gasto de tempo de projeto em correção de bugs.
Utilização de classes da Camada Model
Uma das formas de evitar que existam muitos parâmetros é utilizar a própria classe model base como Filtro. Assim, no método de exemplo, ao invés de passarmos 7 parâmetros, poderíamos passar apenas 1, uma instância de Endereco, que naturalmente já contém todos os campos de filtros, conforme mostra o código abaixo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
//Imports necessarios..... import java.util.Collection; import org.apache.commons.lang3.StringUtils; import org.hibernate.Criteria; import org.hibernate.Session; import org.hibernate.criterion.MatchMode; import org.hibernate.criterion.Restrictions; //..... definicao da classe e outras coisas @SuppressWarnings("unchecked") public Collection<Endereco> findByFilter(Endereco endereco) { Session session = HibernateUtil.getSession(); try { Criteria c = session.createCriteria(this.entityClassName, "endereco"); if (StringUtils.isNotBlank(endereco.getBairro())) { c.add(Restrictions.like("bairro", endereco.getBairro(), MatchMode.ANYWHERE)); } if (StringUtils.isNotBlank(endereco.getCidade())) { c.add(Restrictions.like("cidade", endereco.getCidade(), MatchMode.ANYWHERE)); } if (StringUtils.isNotBlank(endereco.getComplemento())) { c.add(Restrictions.like("complemento", endereco.getComplemento(), MatchMode.ANYWHERE)); } if (StringUtils.isNotBlank(endereco.getEstado())) { c.add(Restrictions.like("estado", endereco.getEstado(), MatchMode.ANYWHERE)); } if (StringUtils.isNotBlank(endereco.getPais())) { c.add(Restrictions.like("pais", endereco.getPais(), MatchMode.ANYWHERE)); } if (StringUtils.isNotBlank(endereco.getRua())) { c.add(Restrictions.like("rua", endereco.getRua(), MatchMode.ANYWHERE)); } if (endereco.getNumero() != null) { c.add(Restrictions.eq("numero", endereco.getNumero())); } return c.list(); } catch (Exception e) { e.printStackTrace(); return null; } finally { session.close(); } } |
Repare que as linhas marcadas foram alteradas, mas apenas porque agora nós estamos buscando as informações a partir da instância de endereco e não mais de cada parâmetro individualmente.
Mas e se o filtro de busca conter outros campos que não estão presentes na classe model?
Por exemplo, se o cliente pedir para que dentro do filtro fosse adicionado o campo de nome do cliente. Não seria possível adicionar esse nome do cliente na classe model Endereco, pois seria um campo apenas para esse filtro de busca desta tela de pesquisa. Assim é necessário, ao adotar essa solução de utilizar uma classe model, verificar se não haverão problemas com relação a inserção de novos campos de busca nesse filtro.
Utilização de classes Data Transfer Object
Antes de continuar a leitura…
Nesse artigo utilizamos o pattern DTO. Se você não sabe o que significa DTO, por favor leia o artigo.
Uma outra forma de utilizar é criar uma classe de Data Transfer Object ou DTO. A classe EnderecoDTO seria um bom substituto para o problema relacionado no tópico anterior, o qual poderíamos no futuro adicionar novos campos de acordo com a necessidade sem impactar em outros aspectos do projeto, evitando assim problemas de adicionar campos em classes que representam a abstração negocial do projeto. O código abaixo mostra a alteração ao inserir a classe DTO, repare que o código permaneceu idêntico ao código anterior, somente mudando o tipo da classe no parâmetro do método.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
//Imports necessarios..... import java.util.Collection; import org.apache.commons.lang3.StringUtils; import org.hibernate.Criteria; import org.hibernate.Session; import org.hibernate.criterion.MatchMode; import org.hibernate.criterion.Restrictions; //..... definicao da classe e outras coisas @SuppressWarnings("unchecked") public Collection<Endereco> findByFilter(EnderecoDTO endereco) { Session session = HibernateUtil.getSession(); try { Criteria c = session.createCriteria(this.entityClassName, "endereco"); if (StringUtils.isNotBlank(endereco.getBairro())) { c.add(Restrictions.like("bairro", endereco.getBairro(), MatchMode.ANYWHERE)); } if (StringUtils.isNotBlank(endereco.getCidade())) { c.add(Restrictions.like("cidade", endereco.getCidade(), MatchMode.ANYWHERE)); } if (StringUtils.isNotBlank(endereco.getComplemento())) { c.add(Restrictions.like("complemento", endereco.getComplemento(), MatchMode.ANYWHERE)); } if (StringUtils.isNotBlank(endereco.getEstado())) { c.add(Restrictions.like("estado", endereco.getEstado(), MatchMode.ANYWHERE)); } if (StringUtils.isNotBlank(endereco.getPais())) { c.add(Restrictions.like("pais", endereco.getPais(), MatchMode.ANYWHERE)); } if (StringUtils.isNotBlank(endereco.getRua())) { c.add(Restrictions.like("rua", endereco.getRua(), MatchMode.ANYWHERE)); } if (endereco.getNumero() != null) { c.add(Restrictions.eq("numero", endereco.getNumero())); } return c.list(); } catch (Exception e) { e.printStackTrace(); return null; } finally { session.close(); } } |
finnaly{
Como o criteria por default não necessita primeiro gerar a Query para depois adicionar os parâmetros, não existe adição de WHERE, AND ou os próprios parâmetros para criar um Map. Assim somente a troca de N parâmetros para uma classe DTO já basta para uma boa organização do método
Duvidas ou sugestões? Deixe seu feedback! Isso ajuda a saber a sua opinião sobre os artigos e melhorá-los para o futuro! Isso é muito importante!
Até um próximo post!
Leave a Reply