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: