No post Bastidores do Spring Roo: A camada de persistência descrevi como o Spring Roo cria e mantém a camada de persistencia de um aplicativo Java para Web, usando como exemplo um CRUD de Produtos. Porém, deixei de fora propositalmente, algumas funcionalidades interessantes do Roo ainda na área de persistência. Nesse post vou demonstrar como implementar funcionalidade de pesquisa em uma aplicação através do Spring Roo.
O Spring Roo disponibiliza o comando finder para criar consultas via linha de comando, no shell. O finder gera toda estrutura necessária para implementar uma funcionalidade de pesquisa em uma entidade, desde o método Java com código JPA-QL, passando pela controller até chegar na página web.
Demo: a entidade Produto
Para testar essa funcionalidade do Roo, vou relembrar um trecho do exemplo usado no post anterior, a criação da entidade Produto. Os comandos a seguir são executados no shell do Roo para criar Produto com descrição e o valor:
O finder pode ser usado de duas maneiras, a primeira delas para listar quais são as opções de consulta a partir de uma entidade. Veja o exemplo:
O finder list mostra as opções de consultas do Produto levando em consideração os atributos definidos na classe. A propriedade class é opcional, não precisa ser indicada se o cursor, no shell, estiver posicionado na entidade. Esse comando suporta outras duas propriedades:
Agora que sabemos quais são as opções de consulta, é possível usar o outro comando do finder para criar a consulta. No exemplo a seguir uso o comando finder add para criar a consulta de Produtos com filtro em preço mínimo e máximo. Na propriedade finderName indicamos qual é a consulta.
Repare no log do shell que o Roo atualizou o menu de navegação, o arquivo properties (i18n), o aspecto da camada controller e criou a página findProdutoesByPrecoBetween.jspx para o usuário executar a consulta.
O Roo muda a anotação @RooEntity da classe Produto, indicando que a entidade utiliza o finder findProdutoesByPrecoBetween. A partir daí o Roo cria um outro aspecto Produto_Roo_Finder.aj para concentrar todas as consultas do Produto, seguindo a mesma estratégia de manipulação do código via aspectos. Veja o código do aspecto:
O método findProdutoesByPrecoBetween será compilado, junto com os outros aspectos, na classe Produto.class. Da mesma forma que ocorre com os outros aspectos do Roo, vale a pena rever o diagrama que ilustra como o Roo manipula o código.
Além do finder
O finder quebra um tremendo galho, mas não cobre todas as possibilidades de consultas. Imagine por exemplo que é necessário criar uma consulta para retornar uma soma dos valores de todos os produtos cadastrados, o finder não gera essa consulta e nesse caso vamos sujar as mãos com um pouco de código. O conteúdo JPA-QL para essa consulta é extremamente simples, o código seria algo como:
Mas a questão aqui é outra, qual é o lugar certo para escrever esse método? Lembre-se de que NUNCA devemos manipular o conteúdo de um aspecto gerado pelo Spring Roo, e de que o Roo não usa DAOs. Seguindo a linha do ActiveRecord, afinal todo código ORM do Roo é compilado na classe da entidade, o lugar mais indicado é a classe Produto.
Uma questão sobre o Design
Explorando um pouco mais esse cenário, pensando no design e organização do projeto, imagine uma aplicação maior, tanto em tamanho quanto em complexidade, com queries criadas pelo finder e outras escritas na mão. Como fica a organização do código?
Me deparei com uma situação dessa em um projeto que desenvolvi com o Roo, naquela ocasião a equipe não se sentiu confortável em lidar com uma parte do código em aspectos e outra no código Java. Em alguns momentos isso gerou uma certa confusão.
A solução mais interessante para aquele caso foi colocar todo o código das consultas, gerados ou não pelo finder, na entidade. Ainda usávamos o finder para gerar boa parte das consultas, mas passamos a copiar os métodos gerados pelo Roo para a classe (entidade) Java.
Uma característica interessante do Roo é de ele não gera código redundante. Caso o desenvolvedor implemente um método que foi, ou ainda será, implemetando pelo Roo a ferramenta descarta a criação desse método, além disso se o método já foi escrito no aspecto o Roo elimina esse código.
Vou discutir sobre engenharia reversa no Roo em um terceiro post!
[]s
Eder Magalhães
www.yaw.com.br
twitter.com/youandwe
twitter.com/edermag
O Spring Roo disponibiliza o comando finder para criar consultas via linha de comando, no shell. O finder gera toda estrutura necessária para implementar uma funcionalidade de pesquisa em uma entidade, desde o método Java com código JPA-QL, passando pela controller até chegar na página web.
Demo: a entidade Produto
Para testar essa funcionalidade do Roo, vou relembrar um trecho do exemplo usado no post anterior, a criação da entidade Produto. Os comandos a seguir são executados no shell do Roo para criar Produto com descrição e o valor:
roo> entity --class ~.model.Produto roo> field string --fieldName descricao --notNull roo> field number --type java.lang.Double --fieldName preco --min 1
O finder pode ser usado de duas maneiras, a primeira delas para listar quais são as opções de consulta a partir de uma entidade. Veja o exemplo:
roo> finder list --class ~.model.Produto findProdutoesByDescricao(String descricao) findProdutoesByDescricaoEquals(String descricao) findProdutoesByDescricaoIsNotNull() findProdutoesByDescricaoIsNull() findProdutoesByDescricaoLike(String descricao) findProdutoesByDescricaoNotEquals(String descricao) findProdutoesByPreco(Double preco) findProdutoesByPrecoBetween(Double minPreco, Double maxPreco) findProdutoesByPrecoEquals(Double preco) findProdutoesByPrecoGreaterThan(Double preco) findProdutoesByPrecoGreaterThanEquals(Double preco) findProdutoesByPrecoIsNotNull() findProdutoesByPrecoIsNull() findProdutoesByPrecoLessThan(Double preco) findProdutoesByPrecoLessThanEquals(Double preco) findProdutoesByPrecoNotEquals(Double preco)
O finder list mostra as opções de consultas do Produto levando em consideração os atributos definidos na classe. A propriedade class é opcional, não precisa ser indicada se o cursor, no shell, estiver posicionado na entidade. Esse comando suporta outras duas propriedades:
- depth: indica a profundidade para as combinações dos atributos, padrão é 1. Veja o que acontece repetindo o comando acima com: --depth 2
- filter: filtro que indica o atributo que o finder deve listar.
roo> finder list --class ~.model.Produto --filter preco findProdutoesByPreco(Double preco) findProdutoesByPrecoBetween(Double minPreco, Double maxPreco) findProdutoesByPrecoEquals(Double preco) findProdutoesByPrecoGreaterThan(Double preco) findProdutoesByPrecoGreaterThanEquals(Double preco) findProdutoesByPrecoIsNotNull() findProdutoesByPrecoIsNull() findProdutoesByPrecoLessThan(Double preco) findProdutoesByPrecoLessThanEquals(Double preco) findProdutoesByPrecoNotEquals(Double preco)
Agora que sabemos quais são as opções de consulta, é possível usar o outro comando do finder para criar a consulta. No exemplo a seguir uso o comando finder add para criar a consulta de Produtos com filtro em preço mínimo e máximo. Na propriedade finderName indicamos qual é a consulta.
roo> finder add --class ~.model.Produto --finderName findProdutoesByPrecoBetween Updated SRC_MAIN_JAVA/br/com/yaw/produtos/model/Produto.java Created SRC_MAIN_WEBAPP/WEB-INF/views/produtoes/findProdutoesByPrecoBetween.jspx Updated SRC_MAIN_WEBAPP/WEB-INF/views/menu.jspx Updated SRC_MAIN_WEBAPP/WEB-INF/views/produtoes/views.xml Updated SRC_MAIN_WEBAPP/WEB-INF/i18n/application.properties Created SRC_MAIN_JAVA/br/com/yaw/produtos/model/Produto_Roo_Finder.aj Updated SRC_MAIN_JAVA/br/com/yaw/produtos/web/ProdutoController_Roo_Controller.ajUm detalhe importante: estou considerando que a camada web do projeto foi criada (controller all).
Repare no log do shell que o Roo atualizou o menu de navegação, o arquivo properties (i18n), o aspecto da camada controller e criou a página findProdutoesByPrecoBetween.jspx para o usuário executar a consulta.
O Roo muda a anotação @RooEntity da classe Produto, indicando que a entidade utiliza o finder findProdutoesByPrecoBetween. A partir daí o Roo cria um outro aspecto Produto_Roo_Finder.aj para concentrar todas as consultas do Produto, seguindo a mesma estratégia de manipulação do código via aspectos. Veja o código do aspecto:
... privileged aspect Produto_Roo_Finder { public static TypedQuery<Produto> Produto.findProdutoesByPrecoBetween(Double minPreco, Double maxPreco) { if (minPreco == null) throw new IllegalArgumentException("The minPreco argument is required"); if (maxPreco == null) throw new IllegalArgumentException("The maxPreco argument is required"); EntityManager em = Produto.entityManager(); TypedQuery<Produto> q = em.createQuery("SELECT p FROM Produto AS p "+ "WHERE p.preco BETWEEN :minPreco AND :maxPreco", Produto.class); q.setParameter("minPreco", minPreco); q.setParameter("maxPreco", maxPreco); return q; } }
O método findProdutoesByPrecoBetween será compilado, junto com os outros aspectos, na classe Produto.class. Da mesma forma que ocorre com os outros aspectos do Roo, vale a pena rever o diagrama que ilustra como o Roo manipula o código.
Além do finder
O finder quebra um tremendo galho, mas não cobre todas as possibilidades de consultas. Imagine por exemplo que é necessário criar uma consulta para retornar uma soma dos valores de todos os produtos cadastrados, o finder não gera essa consulta e nesse caso vamos sujar as mãos com um pouco de código. O conteúdo JPA-QL para essa consulta é extremamente simples, o código seria algo como:
... public static Number getSumPreco() { EntityManager em = Produto.entityManager(); Query q = em.createQuery("SELECT SUM(p.preco) FROM Produto AS p"); return (Number) q.getSingleResult(); } ...
Mas a questão aqui é outra, qual é o lugar certo para escrever esse método? Lembre-se de que NUNCA devemos manipular o conteúdo de um aspecto gerado pelo Spring Roo, e de que o Roo não usa DAOs. Seguindo a linha do ActiveRecord, afinal todo código ORM do Roo é compilado na classe da entidade, o lugar mais indicado é a classe Produto.
Uma questão sobre o Design
Explorando um pouco mais esse cenário, pensando no design e organização do projeto, imagine uma aplicação maior, tanto em tamanho quanto em complexidade, com queries criadas pelo finder e outras escritas na mão. Como fica a organização do código?
Me deparei com uma situação dessa em um projeto que desenvolvi com o Roo, naquela ocasião a equipe não se sentiu confortável em lidar com uma parte do código em aspectos e outra no código Java. Em alguns momentos isso gerou uma certa confusão.
A solução mais interessante para aquele caso foi colocar todo o código das consultas, gerados ou não pelo finder, na entidade. Ainda usávamos o finder para gerar boa parte das consultas, mas passamos a copiar os métodos gerados pelo Roo para a classe (entidade) Java.
Uma característica interessante do Roo é de ele não gera código redundante. Caso o desenvolvedor implemente um método que foi, ou ainda será, implemetando pelo Roo a ferramenta descarta a criação desse método, além disso se o método já foi escrito no aspecto o Roo elimina esse código.
Vou discutir sobre engenharia reversa no Roo em um terceiro post!
Outras referências
- Posts do Spring Roo no Globalcoders;
- Documentação de referência do Spring Roo;
- Twitter do Roo;
[]s
Eder Magalhães
www.yaw.com.br
twitter.com/youandwe
twitter.com/edermag
Comentários