Caso você use Hibernate, EclispeLink, ou qualquer implementação da JPA, já deve ter percebido que a maioria dos métodos dos seus DAO’s acabam parecidos com o que segue.
@Repository public class ProductDAO { @PersistenceContext private EntityManager manager; public void save(Product produto) { manager.persist(produto); } public List<Product> list() { return manager.createQuery( "select p from Product p", Product.class).getResultList(); } public Product find(Integer id) { TypedQuery<Product> query = manager .createQuery( "select distinct(p) from Product p join fetch p.prices where p.id=:id", Product.class).setParameter("id", id); return query.getSingleResult(); } public Product findBy(Integer id, BookType bookType) { TypedQuery<Product> query = manager .createQuery( "select p from Product p join fetch p.prices price where p.id = :id and price.bookType = :bookType", Product.class); query.setParameter("id", id); query.setParameter("bookType", bookType); return query.getSingleResult(); } public BigDecimal sumPricesPerType(BookType bookType) { TypedQuery<BigDecimal> query = manager.createQuery( "select sum(price.value) from Product p join p.prices price where price.bookType =:bookType", BigDecimal.class); query.setParameter("bookType", bookType); return query.getSingleResult(); } }
Perceba que em geral só mudam as strings das queries. Para facilitar a criação de DAO’s que acessam bancos relacionais e inclusive outros tipos de bancos, o Spring possui um projeto chamado Spring Data. Como no nosso caso estamos usando a JPA, ficamos com a extensão Spring Data JPA. Para o mesmo exemplo de cima, nosso código ficaria da seguinte forma.
@Repository public interface ProductDAO extends CrudRepository<Product, Integer>{ @Query("select sum(price.value) from Product p join p.prices price where price.bookType = :book") public BigDecimal sumPricesPerType(@Param("book") BookType book); }
Perceba que tiramos os métodos mais simples, já que todos eles já existem na interface. As queries que são específicas do nosso sistema, podemos mapear através da annotation @Query. Ainda temos mais alguns detalhes aqui que são interessantes. A query do método sumPricesPerType espera um argumento chamado book e precisamos informar para o Spring Data que o parâmetro do nosso método vai ser usado para preencher essa necessidade. Esse é o objetivo da annotation @Param. Para finalizar, a interface CrudRepository espera dois tipos genéricos. O primeiro indica o tipo de objeto que estamos lidando, já que vamos querer gravar produtos, listar produtos etc. O segundo indica o tipo do atributo que representa o id da classe que, no nosso caso, é um Integer.
Uma curiosidade, não sei se você também sentiu. Já que isso é apenas uma interface, onde está a implementação? Aí entra a mágica da geração dos proxies dinâmicos. O Spring gera o bytecode da implementação em tempo de execução e por isso, para vários casos, não temos a necessidade de criar a implementação na mão. Isso já é suportado no próprio Java e nem é necessário bibliotecas extras para a realização do trabalho.
Ainda temos outras possibilidades que podem ser úteis para você. Imagine que você precisa de uma busca de livros que tenham mais que um certo número de páginas. O projeto já suporta certos padrões de nomes que podem facilitar o trabalho para escrever estas queries.
public List<Product> findByPagesGreaterThan(@Param("pages") int pages);
O findBy é retirado da jogada pelo Spring Data e o resto é parseado para formar a query. Essa parte está mais detalhada na documentação e as possibilidades de busca estão explicadas aqui. A ideia é que você realmente escreva as queries necessárias e não gaste tempo com trabalho repetitivo. Caso você passe um nome de atributo que não existe, será lançada uma exception em tempo de execução.
Toda essa abordagem é extremamente válida, mas pense no seguinte. A interface CrudRepository possui diversos métodos. Alguns exemplos:
- deleteAll
- count
- delete(id)
E se sua aplicação não quiser liberar essas operações sobre os produtos. Como fazer para que os programadores tenham menos possibilidades de chamá-los indevidamente? Para esse caso, podemos usar uma solução interessante.
@org.springframework.stereotype.Repository public interface ProductDAO extends Repository<Product, Integer>{ public Product findOne(Integer id); @Query("select sum(price.value) from Product p join p.prices price where price.bookType = :book") public BigDecimal sumPricesPerType(@Param("book") BookType book); public List<Product> findByPagesGreaterThan(@Param("pages") int pages); public List<Product> findAll(); public Product save(Product product); }
Ao invés de herdarmos da interface CrudRepository, herdamos da interface mais simples Repository. E agora só expomos os métodos que queremos! Repare que apenas copiamos os métodos que gostaríamos que fossem herdados da CrudRepository, esse passo é muito importante! Caso você adicione um método na interface que não tem um mapeamento automático, e não tenha adicionado a annotation @Query, acontecerá uma exception igual a lançada quando mapeamos erroneamente o nome do atributo. Caso você use a Spring IDE, ganhará essas validações em tempo de compilação.
Para isso tudo funcionar, é necessário que adicionemos a dependência do spring-data-jpa no nosso projeto.
<!-- spring data jpa --> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>1.8.0.RELEASE</version> </dependency>
Bem, é isso! Nas próximas semanas, em geral toda segunda, voltarei aqui postando algum assunto sobre o universo Spring que não foi coberto diretamente no meu livro. Eles devem variar em algo que vocês possam usar diretamente nos projetos de vocês assim como assuntos mais ligados a coisas internas do framework, mas que também podem ser interessantes de serem aprendidos.
Caso você tenha algum comentário ou sugestão de assunto não se acanhe, expresse sua vontade na parte de comentários!. O projeto que servirá de base para a maioria dos posts pode ser encontrado aqui.
Sensacional o post. Explica de forma muito intuitiva o uso desta ótima ferramenta.
Aproveito tbm para parabenizá-lo e fazer um pequeno merchan do seu livro Dominando o Spring MVC. Ótimo e recomendadíssimo.
LikeLike
Ainda bem que você gostou, Ewerton! E também muito obrigado por ter lido o livro 🙂
LikeLike
[…] disso, ele também já inclui as dependências para a Spring Data JPA, para facilitar a criação dos seus DAOs. Para completar essa primeira parte da migração do […]
LikeLike
Excelente artigo! Deixou claro para mim alguns detalhes que outros não deixaram.
LikeLike
Muito massa …gostaria de deixar uma sugestão para fazer um post sobre AbstractRoutingDataSource…usando jdbc e jpa junto, roteamento com multiplos datasources..essas implementações são bastante usadas no meio corporativo…valeu
LikeLike
Bacana,
Uma dúvida, utilizando java 8 e a sua biblioteca java time com banco MYSQL, não ocorre erro na persistência, porém os dados são gravados como BLOB.
Necessita fazer alguma configuração na aplicação para associar os tipos entre a classe e a entidade ?
Valeu
LikeLike
[…] primeiro post aqui do blog foi explicado o objetivo e como usar o projeto Spring Data JPA. Desde então já usei […]
LikeLike
Como seria implementado esta ultima interface, estou com problema nisto. pois minha Business tem que chamar um método e não consigo chamar isso na interface, eu teria que implementar essa interface.
LikeLike
Oi Rafael, tudo bem? Você pode ver os passos exatos aqui => https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.custom-implementations O importante ali é o que ele chama de fragmento. Você vai criar uma interface sua, com o método que necessita de implementação específica… vai criar uma classe que implementa essa interface. No fim, vai voltar para sua interface padrão, que vai herdar tanto do Spring Data JPA quanto da sua interface customizada.
LikeLike