Spring Data Rest é vida

Fala aí galera, tudo bem? Meu nome é Fernando e sou instrutor e desenvolvedor na caelumalura.

Não é de hoje que o Spring vem facilitando nossa vida, e vamos combinar que os caras mandam muito bem!

Agora imagina só um mundo perfeito, onde declaramos nossas classes de modelo, repositórios e magicamente temos enpoints para fazermos todas as operações que declaramos nos nossos repositórios (por exemplo incluir, listar, alterar e remover).

Esse mundo perfeito existe e nele essa mágica se chama Spring data rest. Sim, ao utilizarmos ele só de declararmos nossos repositórios temos endpoints disponíveis.

Tá, legal saber que existe o Spring Data Rest.  Mas e como uso ele? (Show me the code!!!)

Bom galera eu montei um exemplo bem básico com Spring Boot Maven e está no meu Github. Nele você vai ver no arquivo pom.xml as seguintes declarações:

Spring Boot

<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>1.4.2.RELEASE</version>
</parent>

E os staters do Spring Data e do Spring data Rest

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

Além dos Starters tenho também o h2 como banco de dados em memória e o lombok.

<dependency>
	<groupId>com.h2database</groupId>
	<artifactId>h2</artifactId>
</dependency>

<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
</dependency>

Para quem não conheçe o lombok, ele nos ajuda a escrever menos código repetitivo por exemplo: getter, setter, construtor, toString, equals/hashcode e etc...

Com ele anotamos nossos atributos com @Getter e/ou @Setter, anotamos nossa classe com @NoArgsConstructor ou @RequiredArgsConstructor, @AllArgsConstructor e ao compilarmos nosso código ele gera os byte-codes equivalentes.Vale a pena dar uma conferida!

No meu caso estou usando a anotação @Data (que engloba um monte dessas anotações) e @NoArgsConstructor (para ter um construtor sem parâmetros, por conta dos frameworks).

(OBS: Para rodar o projeto por dentro do IDE você precisa instalar o agent lombok, do contrário seu IDE não vai encontrar o getters, setters e afins.)

Em java temos duas classes de modelo Book e Author.

@Entity
@Data
@NoArgsConstructor
public class Author {

	@Id
	@GeneratedValue
	private Long id;

	@NotBlank
	private String name;

}
@Entity
@Data
@NoArgsConstructor
public class Book {

	@Id
	@GeneratedValue
	private Long id;

	@NotBlank
	private String title;

	@NotBlank
	private String description;

	@Min(50)
	private Integer numOfPages;

	@Valid
	@ManyToMany(fetch=FetchType.EAGER)
	private Set<Author> authors = new HashSet<>();

	public void add(Author author) {
		authors.add(author);
	}

}

Além do modelo tenho também um repositórios para cada, BookRepository e AuthorRepository

public interface BookRepository extends CrudRepository<Book, Long> {
}
public interface AuthorRepository extends CrudRepository<Author, Long>{
}

Para subir a aplicação tenho a classe Application que inicia o Spring Boot e salva um livro e um autor no banco de dados;

@SpringBootApplication
public class Application {

	public static void main(String[] args) throws Exception {
		SpringApplication.run(Application.class, args);
	}

	@Autowired
	private BookRepository bookRepository;

	@Autowired
	private AuthorRepository authorRepository;

	@PostConstruct
	@Transactional
	public void onLoad(){

		Author alberto = new Author();
		alberto.setName("Alberto");
		authorRepository.save(alberto);

		Book book = new Book();
		book.setTitle("Spring MVC");
		book.setDescription("Domine o principal framework web Java");
		book.setNumOfPages(237);

		book.add(alberto);

		bookRepository.save(book);

	}

}

E pronto! Temos uma endpoints para efetuarmos o CRUD de livros e autores. (Spring Data Rest é ou não é vida?)

Ao acessarmos http://localhost:8080, temos o seguinte retorno

{
"_links" : {
"authors" : {
"href" : "http://localhost:8080/authors&quot;
},
"books" : {
"href" : "http://localhost:8080/books&quot;
},
"profile" : {
"href" : "http://localhost:8080/profile&quot;
}
}
}

O Spring Data Rest usa fortemente os conceitos de HATEOAS que a grosso modo diz que através de um recurso disponível deve ser possível navegar pelo estado desse recurso através dos links. Por padrão o spring usa um modelo de apresentação chamado HAL para representação da resposta.

Bom os endpoints /authors e /books me parecem bem ok, o que deve ter neles, mas e o endpoint /profile?

Esse enpoint tem as meta-informações do seus serviços, por exemplo ao acessarmos http://localhost:8080/profile/authors temos o seguinte resultado

{
"alps":{
"version":"1.0",
"descriptors":[
{
"id":"author-representation",
"href":"http://localhost:8080/profile/authors&quot;,
"descriptors":[
{
"name":"name",
"type":"SEMANTIC"
}
]
},
{
"id":"create-authors",
"name":"authors",
"type":"UNSAFE",
"rt":"#author-representation"
},
{
"id":"get-authors",
"name":"authors",
"type":"SAFE",
"rt":"#author-representation"
},
{
"id":"patch-author",
"name":"author",
"type":"UNSAFE",
"rt":"#author-representation"
},
{
"id":"update-author",
"name":"author",
"type":"IDEMPOTENT",
"rt":"#author-representation"
},
{
"id":"get-author",
"name":"author",
"type":"SAFE",
"rt":"#author-representation"
},
{
"id":"delete-author",
"name":"author",
"type":"IDEMPOTENT",
"rt":"#author-representation"
}
]
}
}

Essas meta-informações estão associadas a qual representação temos no enpoint (no nosso caso)  authors e toda a definição dessa representação (os atributos e métodos). O endpoint profile é definido na RFC 6906.

Dentro da representação do author temos um objeto com id author-representation e nele temos um descritor com nome name e o tipo dele é SEMANTIC.
Esse tipo SEMANTIC indica um elemento que guarda um estado (atributo).

Além desse objeto temos um objeto com id create-authors, get-authors ou update-author ambos com nome author porém com tipos diferentes (nesse caso temos UNSAFE, SAFE e IDEMPOTENT). Esse objetos são as operações que podemos fazer sobre essa representação (methods).

Cada tipo tem um comportamento diferente:

SAFE – operações desse tipo são idempotentes e não alteram estado no servidor (GET, HEAD).

UNSAFE – operações desse tipo não são idempotentes e alteram estado no servidor (POST).

IDEMPOTENT – operações desse tipo são idempotentes e alteram estado no servidor (PUT, DELETE).

Uma operação é considerada idempotente se o resultado dele for bem sucedida indiferente do numero de vezes que ela for executada.

Agora sim, vamos para os endpoints mais “óbvieis“. Irei utilizar o programa curl para efetuar as chamadas para nosso endpoints.

Para listar nosso autores podemos efetuar a seguinte requisição:

curl -X GET http://localhost:8080/authors

(Deixei explicito o verbo GET somente por questões didáticas).

Nesse caso temos o seguinte resultado:

{
"_embedded" : {
"authors" : [ {
"name" : "Alberto",
"_links" : {
"self" : {
"href" : "http://localhost:8080/authors/1&quot;
},
"author" : {
"href" : "http://localhost:8080/authors/1&quot;
}
}]
},
"_links" : {
"self" : {
"href" : "http://localhost:8080/authors&quot;
},
"profile" : {
"href" : "http://localhost:8080/profile/authors&quot;
}
}
}

Nesse caso temos um autor com o nome Alberto e os links do autor levam ao recurso dele mesmo. E os links da representação como um todo levam a lista de autores ou ao profile de autores.

Vamos seguirmos o link para listar somente o autor Alberto.

curl -X GET http://localhost:8080/authors/1

E nesse caso temos o seguinte resultado:

{
"name" : "Alberto",
"_links" : {
"self" : {
"href" : "http://localhost:8080/authors/1&quot;
},
"author" : {
"href" : "http://localhost:8080/authors/1&quot;
}
}
}

E para criar um novo autor? bom para isso temos que usar o verbo POST.

curl -X POST -H "Content-type: application/json" -d '{"name": "Fernando"}' http://localhost:8080/authors

Agora temos dois autores, e para alterar? Fácil né, só usar outro verbo. Nesse caso o PATCH

 curl -X PATCH -H "Content-type: application/json" -d '{"name": "Fernando Willian"}' http://localhost:8080/authors/2

E para excluir vamos usar o verbo DELETE

curl -X DELETE http://localhost:8080/authors/2

Podemos usar as mesmas operações para os livros.

Para deixar um pouco mais interessante, vamos adicionar um pouco de segurança ao nossos endpoints.
Para isso vamos adicionar o starter do Spring Security no nosso arquivo pom.xml:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>

E vamos configurar como será nossa autenticação e quais os endereços que queremos proteger:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.inMemoryAuthentication()
		.withUser("common").password("123").roles("USER").and()
		.withUser("admin").password("@dm1n").roles("ADMIN");
		
	}
	
	
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
			.httpBasic()
			.and()
				.authorizeRequests()
					.antMatchers(HttpMethod.POST, "/**").hasRole("ADMIN")
					.antMatchers(HttpMethod.PATCH, "/**").hasRole("ADMIN")
					.antMatchers(HttpMethod.DELETE, "/**").hasRole("ADMIN")
				.and()
					.csrf()
						.disable();
	}
	
}

Nessa configuração temos que nos autenticar com uma conta com permissão de ADMIN para efetuar qualquer requisição com os verbos POST, PATCH e DELETE.

Agora para incluir um novo autor devemos nos autenticar:

curl -X POST -H "Content-type: application/json" -d '{"name": "Fernando"}' http://localhost:8080/authors -u admin:@dm1n

O mesmo se aplica para alteração ou exclusão.

E aí o que achou do Spring Data Rest? Já o conhecia?

Advertisements

Começando com a magia do Spring Boot

No livro sobre Spring que escrevi para a Casa do Código e no SetupMyProject fiz a opção pelo uso normal das extensões do Spring. Para criar o projeto web, usei o Spring MVC no seu modo convencional configurando tudo que era necessário, assim como fiz para a parte da JPA, Spring Security etc. No fim acho realmente importante que as pessoas conheçam bem as tecnologias que usam, para terem um olhar crítico sobre a ferramenta e poderem achar os pontos de extensão necessários para os projetos do dia a dia.

Na época da escrita do livro, o time do Spring já tinha lançado uma outra extensão chamada Spring Boot, cujo objetivo era acelerar o processo de configuração da aplicação, estabelecendo configurações defaults baseadas nas experiências dos desenvolvedores do próprio Spring, assim como pelo feedback provido pelos usuários do framework. Optei por não usá-lo, justamente pelos motivos citados no primeiro parágrafo. Agora, com tudo mais sedimentado e com um conhecimento realmente bom do framework resolvi que era hora de começar a construir uma aplicação com o Spring Boot, já que, principalmente durante a escrita do livro, fui obrigado a navegar muito pelos internals do Spring e tudo mais. Peguei como exemplo a própria app do livro, ela possui regras boas o suficiente para me fazer passar por diversos problemas. O código inicial já está no github.

Então vamos começar! O primeiro passo, como não poderia ser diferente, é a criação do projeto. Você pode realizar este passo no próprio SetupMyProject. O zip vai vir com o mínimo configurado, mas é aí que você já vai perceber a diferença. Para conseguir subir o servidor e acessar a primeira rota, só precisamos de uma classe configurada no projeto.

	@SpringBootApplication
	@Controller
	public class CasadocodigoSpringbootApplication {

		@RequestMapping("/")
		@ResponseBody
		public String index(){
			return "funciona?";
		}

		public static void main(String[] args) {
			SpringApplication.run(CasadocodigoSpringbootApplication.class, args);
		}
	}

Aqui já temos um pouco de mágica. Você vai rodar sua aplicação web a partir de um bom e velho método main. A classe SpringApplication vai ler as configurações da classe passada como argumento e pronto, você tem a sua aplicação no ar. A outra parte da mágica acontece por conta da annotation @SpringBootApplication. Ela é um Sterotype do Spring Boot que já encapsula algumas outras annotations, como a @EnableAutoConfiguration. Essa última, por sua vez, carrega a AutoConfigurationPackages que é responsável por configurar os pacotes que devem ser escaneados, baseados na localização da sua classe.

Um outro ponto bem impressionante é o pom.xml criado, você vai perceber que ele possui pouquíssimas dependências! Vamos dar uma rápida olhada.

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.3.0.BUILD-SNAPSHOT</version>
	</parent>	

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
	</dependencies>

Perceba que você adiciona algumas dependências que eles chamam de starters. A grande sacada é fazer com esses artefatos já baixem tudo que você precisa. E aqui, eu preciso dizer que, pelo menos para mim, eles tomaram uma decisão bastante acertada. Ao invés de te dar a opção de escolher entre vários frameworks, servidores web, libs específicas e etc, eles tomaram algumas decisões e empurraram pra gente. Para a maioria dos projetos isso não muda e, se for preciso, você pode sobreescrever tudo que eles fizeram. A motivação deles foi a mesma que tivemos ao construir o SetupMyProject. Já estamos cheios de frameworks, precisamos é de uma maneira mais fácil de usá-los.

Por mais que já tenhamos o projeto configurado, provavelmente esse não é bem o modelo que vamos seguir. Os nossos controllers não vão ser declarados dentro da mesma classe de configuração. O normal é criar uma outra classe que representa este seu controller.

        @Controller
	@Transactional
	@RequestMapping("/produtos")
	public class ProductsController {

		@Autowired
		private ProductDAO products;
		@Autowired
		private FileSaver fileSaver;
		@Autowired
		private ServletContext ctx;
		@Autowired
		private InternalResourceViewResolver resolver;

		@RequestMapping(method=RequestMethod.GET)
		@Cacheable(value="lastProducts")
		public ModelAndView list() throws ServletException, IOException{
			System.out.println("ola");
			ModelAndView modelAndView = new ModelAndView("products/list");
			modelAndView.addObject("products", products.findAll());
			return modelAndView;
		}

		...

	}

E agora é só pedir para escanear o pacote… Opa, não precisa mais! Como eu criei esse classe em um pacote abaixo do pacote da classe de configuração, todas as classes já vão ser escaneadas de maneira automática. Agora é necessário configurar o acesso a JPA e, nesse momento, nossos corações vão ficar felizes novamente. O nosso único trabalho é adicionar um starter no pom.xml. 

	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-data-jpa</artifactId>
	</dependency>

Quase tudo que você acha que deveria fazer para configurar o acesso ao banco com a JPA, vai ser feito automagicamente. Não será necessário configurar persistence.xml, EntityManagerFactory, EntityManager, PlatformTransactionManager nem nada, tudo vai ser feito pra você. A única coisa que você precisa entregar a ele é o DataSource com os  dados de acesso ao banco. Podemos criar um método com essa configuração na própria classe que contém nosso main. 

@SpringBootApplication
	public class CasadocodigoSpringbootApplication {
		@Bean
		public DataSource dataSource(Environment environment) {
			DriverManagerDataSource dataSource = new DriverManagerDataSource();
			dataSource.setDriverClassName("com.mysql.jdbc.Driver");
			dataSource.setUrl("jdbc:mysql://localhost:3306/casadocodigo");
			dataSource.setUsername("root");
			dataSource.setPassword("");
			return dataSource;
		}

		public static void main(String[] args) {
			SpringApplication.run(CasadocodigoSpringbootApplication.class, args);
		}
	}

Além 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 projeto, foi necessário fazer uma pequena configuração extra para os jsps. O Spring Boot sugere que você use outra tecnologia de view, mas como o jsp era a utilizada no projeto do livro, eu resolvi ir com ela mesmo. Como o tomcat está sendo executado de forma embedded, é necessário que adicionemos uma nova dependência no pom, que é a do compilador de jsps para este tipo de cenário.

	<dependency>
		<groupId>org.apache.tomcat.embed</groupId>
		<artifactId>tomcat-embed-jasper</artifactId>
		<scope>provided</scope>
	</dependency>

Além disso, como é procedimento normal, precisamos configurar um prefixo e um sufixo para busca da nossa view. Podemos fazer isso do jeito tradicional, adicionando um método que retorna um InternalResourceViewResolver. Só que podemos aproveitar a oportunidade para já apresentar um outro detalhe que você pode tirar proveito, que são os arquivos de configuração externos. Você pode criar um arquivo chamado application.properties na raiz do seu classpath, geralmente em src/main/resources. Lá você pode colocar configurações específicas das tecnologias utilizadas no projeto, abaixo segue o meu exemplo.

  spring.mvc.view.prefix=/WEB-INF/jsp/
  spring.mvc.view.suffix=.jsp

  #hibernate
  spring.jpa.hibernate.ddl-auto=update

Perceba que ele é bem diferente de um spring-context.xml da vida. Você não tem que declarar um bean para ser usado, simplesmente configura as propriedades do bean já escolhido. 

Para fechar com chave de ouro, pelo menos para mim :), eles deram mais um passo pensando na vida fácil do usuário. Nessa semana eles lançaram um novo starter chamado de DevToolsUma facilidade adicionada foi a de recarregamento do projeto para cada mudança que você faz no código. Só que, como eles mesmos disseram, esse recarregamento é mais esperto do que um simples hot deploy de um servidor web tradicional. Além disso, ainda adicionaram o suporte para o Live Reloadque possibilita o recarregamento automático das páginas no seu navegador sempre que você alterar uma classe sua no servidor!

Ufa! Falei bastante hoje :). Acho o Spring Boot um projeto muito útil e que, com certeza, você devia dar uma olhada. É um approach que por muito eu esperava no mundo Java e espero que influencie outros projetos a fazer o mesmo. Por fim, acho importante lembrar que você deve continuar procurando o motivo do acontecimento das coisas, para não ficar um “programador de Spring Boot” ao invés de um programador  Java.

Push notifications com Server Sent Events na última versão do Spring

Hoje em dia usamos muitas aplicações onde ficamos na mesma tela por muito tempo. Por exemplo, ficamos na home do facebook e somos notificados de novos posts sem termos a necessidade de ficar atualizando a tela. A mesma coisa acontece na aplicação web do Whatsapp. Um bom questionamento é: como que essas aplicações conseguem ficar fazendo isso? Bem antigamente a técnica mais utilizada era realizar requisições ajax de tempos em tempos para o servidor. O grande problema era que o cliente ficava enviando inúmeros requests sem necessidade, obrigando o servidor a processar todos, sem mesmo ter uma necessidade.

A solução é até meio simples. Ao invés do cliente ficar perguntando para o servidor o tempo inteiro se chegou alguma mensagem, o servidor poderia notificar o cliente quando cada mensagem fosse chegando. Solução conhecida como push notification. Desse jeito o servidor não é inundado de requisições inúteis feitas pelo cliente e, além disso, o cliente é notificado quando realmente uma nova mensagem chega. Ao invés de ficar esperando um certo tempo para poder fazer uma nova requisição pedindo pelas novidades. Para a nossa sorte, o HTML5 especificou duas maneiras para possibilitar as notificações enviadas diretamente pelos servidores.

Provavelmente a mais conhecida é a WebSocket, onde tanto o cliente pode enviar mensagens para o servidor, quanto o servidor pode enviar mensagens para o cliente. Esse é o caso necessário para a aplicação web do Whastapp. Caso você a utilize, basta abrir a sua ferramenta favorita para inspecionar o navegador que você vai encontrar a url wss://w5.web.whatsapp.com/ws. É através dela que você envia e recebe as mensagens.

Agora vamos imaginar uma situação na Casa do Código. Precisamos adicionar uma funcionalidade onde o administrador da loja é capaz de surpreender os atuais visitantes com uma promoção relâmpago de algum livro. Perceba que não queremos que o navegador envie nenhuma mensagem para o servidor e sim, que apenas o servidor notifique o navegador de algum evento. Para este tipo de cenário, podemos uma outra especificação que veio no HTML5 chamada Server Sent Event. E agora a parte mais interessante, a última versão do Spring já vem com suporte nativo para essa outra modalidade de push notification. 

Vamos começar pela método do controller que é responsável pela geração da notificação.

        ...
        public class ProductsController {
         ...
         public static final SseEmitter notifier = new SseEmitter();

	 @RequestMapping("habilita/promocao/{id}")
	 @ResponseStatus(value=HttpStatus.OK)
	 public void enableQuickPromoForProduct(@PathVariable("id") Integer id) throws IOException{
		Product product = products.findOne(id);
		notifier.send(new QuickPromoData(product,messageSource));
	 }
        }

Buscamos um produto e geramos um outro objeto do tipo QuickPromoData. Esse objeto tem apenas as informações necessárias que devem ser enviadas para o cliente. O mesmo vai ser serializado como um JSON, então devemos ter cuidado para só expor o necessário e não ficar gerando mais dados do que precisamos. Sem contar que trabalhar diretamente com o objeto gerenciado pelo hibernate, pode nos levar a problemas de lazy load que não tem nada relacionado com a situação que estamos tratando aqui. É uma ótima prática sempre pensar com bastante carinho nos objetos que serão serializados, ao invés de ficar usando diretamente o objeto de domínio. Além disso estamos trabalhando com uma outra variável chamada notifier.  Ela é do tipo SseEmitter, justamente uma nova classe criada pelo Spring para que possamos enviar os dados para o navegador no protocolo exigido pelo SSE. 

E aqui precisamos parar um segundo para discutir o uso do objeto do tipo SseEmitter. Ele funciona como se fosse seu canal de comunicação com todos os clientes que estão conectados na aplicação naquele momento. Por conta disso precisamos manter o estado desse objeto entre as várias requisições. Na verdade não é nada complicado, basta que deixemos essa variável como estática no próprio controller. Caso isso te incomode você também pode deixá-la como variável de instância, já que todo controller por default vive no escopo da aplicação.

Agora que já notificamos os clientes, precisamos que eles abram a conexão com nosso servidor para que possam ser avisados das novas promoções.

    <script>
	var promoSource = new EventSource("${spring:mvcUrl('PC#enableQuickPromoNotifier').build()}");
	promoSource.onmessage = function(event) {
		var data = event.data;
		console.log(data)
	};
   </script>

A classe EventSource está disponível na maioria dos navegadores. O uso dela é bem direto, basta que instanciemos ele passando a url que abre a conexão que deve ser mantida para o recebimento de dados, também chamada de long pooling. Depois disso, associamos uma função que vai ser chamada sempre que nossa aplicação notificar o canal de comunicação. Para concluir, precisamos do método que trata o request solicitando a abertura da conexão.

     @RequestMapping("habilita/promocao")
     public SseEmitter enableQuickPromoNotifier(){
        return notifier;
     }

Perceba que apenas retornamos o objeto do tipo SseEmitter. Apenas reforçando que é importante que você mantenha o estado desse objeto. Você tem que utilizar a mesma instância retornada por este método para enviar as notificações.

Por fim é necessário que a versão do Spring atualizada no seu projeto. Nesse exemplo foi utilizado o update 4.2.0.RC1. Apenas para lembrar, os posts no blog se baseiam no projeto que está no github.

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.