Facilite seus DAOs com o Spring Data JPA

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 JPAPara 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);

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:

  1. deleteAll
  2. count
  3. 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.

 

9 thoughts on “Facilite seus DAOs com o Spring Data JPA

  1. 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.

    Like

  2. 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

    Like

  3. 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

    Like

  4. 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.

    Like

Leave a comment