Spring Boot: Como criar um servidor REST com Spring Data

Esse artigo criará e configurará um servidor REST com o Spring Data para a manipulação de dados com o banco de dados PostgreSQL e o Spring Boot utilizado para configurar e inicializar a aplicação de forma fácil e rápida.

O Spring Data é um conjunto de projetos do Spring para a manipulação de dados de diversas formas, entre elas em bancos de dados relacionais como o MySQL e o PostgreSQL e em bancos de dados NoSQL com o MongoDB e o Redis. Um desses projetos é o Spring Data JPA para o desenvolvimento de aplicações com a Java Persistence API (JPA), que permite a implementação do mapeamento objeto-relacional de um modelo de dados.

Além do mapeamento, esse framework permite também o desenvolvimento de métodos para o acesso aos dados com pouco ou nenhum código sendo necessário para o seu desenvolvimento, o que facilita muito o desenvolvimento das aplicações, e aumenta a produtividade dos programadores.

Combinado com o Spring Data JPA, é possível utilizar o Spring Boot para a configuração fácil e rápida da aplicação e para a disponibilização de um repositório de dados com uma API Rest. O Spring boot permite a execução da aplicação sem a necessidade de nenhuma ferramenta externa e com praticamente nenhuma configuração necessária. Por exemplo, essa ferramenta executa internamente o servidor de aplicação, sem ser necessário nenhuma configuração, ele também facilita a configuração do framework Spring na aplicação, entre muitas outras coisas.

Esse artigo mostrará como configurar a aplicação utilizando o Spring boot, e também como desenvolver uma aplicação utilizando o Spring Data JPA. Também será mostrado o servidor funcionando retornando dados no padrão JSON.

Configuração da Aplicação

A aplicação de exemplo desse artigo foi desenvolvida na IDE Eclipse e utilizando o maven para a configuração das dependências, a Listagem 1 mostra o arquivo pom.xml que deve conter as dependências spring-boot-starter-data-rest que é a biblioteca para a criação do servidor REST com o Spring Boot, a dependência spring-boot-starter-data-jpa que é a dependência do Spring Data e a dependência postgresql que é o Driver para a conexão com o banco de dados.

Listagem 1. Arquivo pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.santana.mobilab</groupId> <artifactId>server</artifactId> <version>0.0.1</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.1.RELEASE</version> </parent> <properties> <java.version>1.8</java.version> </properties> <dependencies> <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> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>9.4-1200-jdbc4</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <repositories> <repository> <id>spring-releases</id> <url>https://repo.spring.io/libs-release</url> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>spring-releases</id> <url>https://repo.spring.io/libs-release</url> </pluginRepository> </pluginRepositories> </project>

Para a execução da aplicação é necessário ter o banco de dados PostgreSQL instalado, caso o leitor prefira, é possível utilizar outro banco de dados como o MySQL ou SQL Server, basta alterar a configuração do pom para importar a dependência do Driver do banco desejado.

Desenvolvimento da Aplicação

Com tudo configurado, podemos começar a implementação da aplicação, para isso serão utilizadas duas classes de modelo, a classe Cliente e a classe Compra para representar os objetos que serão armazenados no banco de dados. A Listagem 2 mostra a classe cliente com os atributos id, nome, endereço e cpf e o relacionamento com uma lista de compras. Essa classe é uma entidade JPA, por isso as anotações @Entity @Id, @GeneratedValue, @OneToMany e @JoinColumn.

package com.devmedia.server.model; import java.util.List; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.OneToMany; @Entity public class Cliente { @Id @GeneratedValue(strategy=GenerationType.AUTO) private Long id; private String nome; private String endereco; private String cpf; @OneToMany(targetEntity=Compra.class, fetch=FetchType.EAGER) @JoinColumn(name="cliente_id") private List<Compra> compras; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public String getEndereco() { return endereco; } public void setEndereco(String endereco) { this.endereco = endereco; } public String getCpf() { return cpf; } public void setCpf(String cpf) { this.cpf = cpf; } public List<Compra> getCompras() { return compras; } public void setCompras(List<Compra> compras) { this.compras = compras; } }
Listagem 2. Classe Cliente

A classe Compra também é uma entidade JPA, e por isso tem as mesmas anotações da classe Cliente. A Listagem 3 mostra o código dessa classe, que tem os atributos id, valorTotal e numeroProdutos.

package com.devmedia.server.model; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class Compra { @Id @GeneratedValue(strategy=GenerationType.AUTO) private Long id; private float valorTotal; private int numeroProdutos; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public float getValorTotal() { return valorTotal; } public void setValorTotal(float valorTotal) { this.valorTotal = valorTotal; } public int getNumeroProdutos() { return numeroProdutos; } public void setNumeroProdutos(int numeroProdutos) { this.numeroProdutos = numeroProdutos; } }
Listagem 3. Classe Compra

Agora, com o Spring Data podemos desenvolver os repositórios que são interfaces onde são definidos os métodos de acesso aos dados, para isso é necessário estender a interface PagingAndSortingRepository, que já disponibiliza diversos métodos para o acesso aos dados, além disso também podemos criar novos métodos como o findByNome e o findByNomeOrderByNome. Com o Spring Data não é preciso implementar mais nada, seguindo o padrão de nomes, o framework já entende o que o método deve fazer. Caso o programador queira implementar alguma consulta muito diferente, é possível utilizar a anotação @Query como mostrado na Listagem 4.

Nessa interface também é necessário colocar a anotação @RepositoryRestResource que indica que será criado um servidor REST a partir dos métodos definidos na interface.

package com.devmedia.server.repository; import java.util.List; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.query.Param; import org.springframework.data.rest.core.annotation.RepositoryRestResource; import com.devmedia.server.model.Cliente; @RepositoryRestResource(collectionResourceRel = "cliente", path = "clientes") public interface ClienteRepository extends PagingAndSortingRepository<Cliente, Long> { /** * Método que retorna uma lista de clientes fazendo a busca pelo nome passado como parâmetro. * * @param name * @return lista de clientes */ List<Cliente> findByNome(@Param("name") String name); /** * Método que retorna o cliente com apenas seu nome fazendo a busca com o id passado como parâmetro. * * @param id * @return cliente do id passado como parâmetro. */ @Query("SELECT c.nome FROM Cliente c where c.id = :id") Cliente findNomeById(@Param("id") Long id); /** * Método que retorna uma lista de clientes fazendo a busca pelo nome passado como parâmetro e ordenando os * clientes pelo nome. * * @param name * @return lista de clientes */ List<Cliente> findByNomeOrderByNome(@Param("name") String name); }
Listagem 4. Classe ClienteRepository

O servidor está praticamente pronto, falta apenas criar a classe para iniciar a aplicação, para isso será utilizado o Spring Boot, basta colocar a linha SpringApplication.run(Application.class, args) no método main da classe, que o Spring Boot inicia o tomcat com a aplicação criada já funcionando. Para indicar que essa é uma classe do Spring Boot, é necessário colocar a anotação @SpringBootApplication. A anotação @EntityScan indica o pacote onde estão as classes JPA e a anotação @EnableJpaRepositories indica o pacote onde estão as classes de repositório. A Listagem 5 mostra o código dessa classe.

package com.devmedia.server.run; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.orm.jpa.EntityScan; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; @SpringBootApplication @EntityScan(basePackages = { "com.devmedia.server.model" }) @EnableJpaRepositories(basePackages = { "com.devmedia.server.repository" }) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
Listagem 5. Classe Application

O último passo no desenvolvimento da aplicação é configurar o banco de dados, para isso é necessário criar o arquivo application.properties, que deve ser criado no diretório src/main/resources do projeto. Nesse arquivo são configurados o driver, a url, o nome de usuário e a senha do banco de dados e a porta que o tomcat será executado. A Listagem 6 mostra um exemplo desse arquivo, caso você esteja usando outro banco de dados, será necessário trocar o driverClasseName e a URL de conexão.

spring.jpa.database=POSTGRESQL spring.datasource.platform=postgres spring.jpa.show-sql=true spring.jpa.hibernate.ddl-auto=create-drop spring.database.driverClassName=org.postgresql.Driver spring.datasource.url=jdbc:postgresql://localhost:5432/priview spring.datasource.username=postgres spring.datasource.password=postgres server.port=8080
Listagem 6. Arquivo application.properties

Para testar a aplicação foi criado um script para a inserção de uma mass de dados. A Listagem 7 mostra esse script, que deve ser executado em alguma interface do PostgreSQL como o PgAdmin.

insert into cliente(id, cpf, endereco, nome) values(1, '1275634645', 'Avenida Paulista 1020', 'Eduardo'); insert into cliente(id, cpf, endereco, nome) values(2, '1234345423', 'Avenida Rebouças 1500', 'Luiz'); insert into cliente(id, cpf, endereco, nome) values(3, '6545645654', 'Rua dos Pinheiros 2000', "Bruna"); insert into cliente(id, cpf, endereco, nome) values(4, '6452345234', 'Alameda Santos 120', "Sonia"); insert into cliente(id, cpf, endereco, nome) values(5, '7565345325', 'Rua XV de Novembro 2012', "Brianda"); insert into cliente(id, cpf, endereco, nome) values(6, '6456563454', 'Rua 7 de Setembro 2001', "Enio"); insert into cliente(id, cpf, endereco, nome) values(7, '7565345645', 'Avenida Brasil 201', "Marcelo"); insert into compra (id, numero_produtos, valor_total, cliente_id) values (1, 5, 100, 1); insert into compra (id, numero_produtos, valor_total, cliente_id) values (2, 2, 500, 1); insert into compra (id, numero_produtos, valor_total, cliente_id) values (3, 7, 600, 2); insert into compra (id, numero_produtos, valor_total, cliente_id) values (4, 2, 200, 2); insert into compra (id, numero_produtos, valor_total, cliente_id) values (5, 8, 300, 2); insert into compra (id, numero_produtos, valor_total, cliente_id) values (6, 9, 400, 3); insert into compra (id, numero_produtos, valor_total, cliente_id) values (7, 3, 300, 4); insert into compra (id, numero_produtos, valor_total, cliente_id) values (8, 2, 200, 4); insert into compra (id, numero_produtos, valor_total, cliente_id) values (9, 9, 400, 4); insert into compra (id, numero_produtos, valor_total, cliente_id) values (10, 8, 1000, 5);
Listagem 7. Inserts para teste da aplicação

Se tudo funcionou corretamente, basta executar a aplicação, o banco de dados será criado automaticamente por causa da opção create-drop colocada no arquivo application.properties. Para acessar os serviços pode ser acessado um browser como o Chrome ou pode ser feita a implementação de um cliente REST também. Por exemplo, o método findByNome foi exposto como um serviço, para acessa-lo basta utilizar a URL http://localhost:8080/clientes/search/findByNome?name=Eduardo. A resposta dessa chamada está na Listagem 8, e ela é enviada na forma de um arquivo JSON. É possível observar que além do cliente, foi retornado também as compras que estão relacionadas ao cliente Eduardo.

{ "_embedded" : { "cliente" : [ { "nome" : "Eduardo", "endereco" : "'Avenida Paulista 1020", "cpf" : "1275634", "compras" : [ { "valorTotal" : 100.0, "numeroProdutos" : 5 }, { "valorTotal" : 500.0, "numeroProdutos" : 2 } ], "_links" : { "self" : { "href" : "http://localhost:8080/clientes/1" }, "cliente" : { "href" : "http://localhost:8080/clientes/1" } } } ] }, "_links" : { "self" : { "href" : "http://localhost:8080/clientes/search/findByNome?name=Eduardo" } } }
Listagem 8. Resposta da chamada ao serviço findByNome

Para consultar todos os serviços disponíveis é possível acessar a URL http://localhost:8080/clientes/search, que retorna todos os serviços disponíveis. A Listagem 9 mostra a resposta dessa solicitação. É possível observar que foram retornados os serviços findNomeByID, findByNomeOrderByNome e findByNome que são os métodos declarados na interface do repositório. Além disso, no arquivo retornado são mostrados os parâmetros necessário para cada serviço, como por exemplo o {?id} e o {?name}.

{ "_links" : { "findNomeById" : { "href" : "http://localhost:8080/clientes/search/findNomeById{?id}", "templated" : true }, "findByNomeOrderByNome" : { "href" : "http://localhost:8080/clientes/search/findByNomeOrderByNome{?name}", "templated" : true }, "findByNome" : { "href" : "http://localhost:8080/clientes/search/findByNome{?name}", "templated" : true }, "self" : { "href" : "http://localhost:8080/clientes/search" } } }
Listagem 9. Resposta da requisição para descobrir serviços disponíveis

Além dos serviços criado, é possível também fazer a busca pelo id do cliente, para isso basta utilizar a URL http://localhost:8080/clientes/id que o servidor retorna um arquivo JSON com todos os dados do cliente daquele id. Por exemplo, na Listagem 10 foi feita a requisição para http://localhost:8080/clientes/2, que retornou os dados do cliente que tem o id=2.

{ "nome" : "Luiz", "endereco" : "'Avenida Rebouças 1500", "cpf" : "1234345", "compras" : [ { "valorTotal" : 600.0, "numeroProdutos" : 7 }, { "valorTotal" : 200.0, "numeroProdutos" : 2 }, { "valorTotal" : 300.0, "numeroProdutos" : 8 } ], "_links" : { "self" : { "href" : "http://localhost:8080/clientes/2" }, "cliente" : { "href" : "http://localhost:8080/clientes/2" } } }
Listagem 10. Resposta da requisição para o cliente de id=2

É possível fazer essa pesquisa em todos os clientes cadastrados na base, por exemplo, na Listagem 11 está o retorno se fizermos com id=3.

{ "nome" : "Bruna", "endereco" : "'Rua dos Pinheiros 2000", "cpf" : "6545645", "compras" : [ { "valorTotal" : 400.0, "numeroProdutos" : 9 } ], "_links" : { "self" : { "href" : "http://localhost:8080/clientes/3" }, "cliente" : { "href" : "http://localhost:8080/clientes/3" } } }
Listagem 11. Resposta da requisição para o cliente de id=3

Não é possível fazer requisições diretamente para os objetos da classe Compra porque não foi implementado um repositório dessa classe, porém, caso o leitor queira, basta criar a classe CompraRepository da mesma forma que foi criada a ClienteRepository, e aí será possível criar serviços também para que o usuário posso acessar diretamente as compras.

Esse artigo mostrou que é bastante simples o desenvolvimento de um servidor REST utilizando os frameworks Spring Data e Spring Boot. A aplicação desenvolvida nesse artigo utilizou o banco de dados PostgreSQL, porém qualquer banco de dados relacional pode ser usado sem praticamente nenhuma alteração no código. Para mostrar o funcionamento do servidor desenvolvido foram mostradas diversas requisições realizadas e o resultado retornado.

Links:

Artigos relacionados