0

API-RestFul-Spring-Boot

Wesley Sousa
Wesley Sousa

Nesse artigo serão abordados conceitos resumidos da criação de uma API Rest usando Spring Boot. Nessa Api será realizado um domínio de cadastro de pessoas. É de importância máxima o entendimento inicial de como o ecossistema Spring se comporta em uma aplicação web. Para melhor entendimento iremos entender toda relação entre as classes dês do momento de inicialização até os comportamentos de integração.

Para iniciar nossa Api entre no site spring initializr (https://start.spring.io/), usaremos o maven para gerenciar nossas dependências,  o spring na sua última versão e o java na versão 11. Baixe as seguintes dependências:

Spring Web; Spring Data JPA; Lombok; H2 Database; Spring Boot Actuador; Validation;

Iremos utilizar o framework Mapstruct, mas para esse primeiro momento não se preocupe em adicionar ao seu pom.xml. Baixe o projeto coloque em um local de sua preferência e importe para a sua IDE de sua preferencia.

Em um projeto spring com a utilização do maven, posterior ao momento da criação do projeto spring através do spring initializr e depois de todas as dependências baixadas e o projeto já importado a IDE, temos os pacotes organizados pelo gerenciador de projeto maven. Dentro da pasta src/main/java o spring já se encarrega de criar sua classe main com os recursos iniciais para subir a aplicação. Nota-se que, existe inclusa uma anotação @springBootApplication,  para o contexto spring essa anotação representa três tipos diferentes: @Configuration, @EnableAutoConfiguration, @ComponentScan.

package com.exemplo.projeto;

 

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

 

@SpringBootApplication

public class PersonapiApplication {

 

    public static void main(String[] args) {

        SpringApplication.run(PersonapiApplication.class, args);

    }

 

}

 

 

 

 

Ao iniciarmos nossa aplicação dentro do pacote principal, o spring irá se encarregar de gerir e criar uma série de procedimentos e comandos necessários para o pleno funcionamento da aplicação. Pois bem, vamos entender de forma sintetizado dês da inicialização do projeto ao momento da construção e execução de uma API rest.

Como funciona esse carregamento ao iniciar aplicação:

@Configuration: a importância dessa anotação tem duas linhas importantes: a primeira é a marcação da classe como uma fonte de definição bean a outra é ligada ao CGLIB.

Um bean é um objeto que é instanciado e montado e gerenciado pelo Spring IoC container dentro do contexto Spring. Dentro do spring os únicos objetos que são gerenciados por ele são os beans. Sendo assim, o Spring IoC  irá buscar as informações em XML annotations ou em código java com as anotações @bean e todo gerenciamento fica de responsabilidade do Spring IoC  para instanciar, montar e relacionar os beans. Vale lembrar que, a relação entre os beans se chama injeção de dependências.

A biblioteca cglib (Code Generation Library). Por ela é permitido manipulação e criação de classes após a fase de compilação de um programa através da instrumentação de bytecode.

Numa forma mais genérica de entender é possível resumir que os beans são a forma como spring irá enxergar as classes, objetos e suas funcionalidades já o CGLP é a forma de invocação de classes ocultas que irão ajudar no momento de subir a aplicação.

@EnableAutoConfiguration : Como a própria definição da anotação que diz,  habilitar auto configuração, esta anotação permite de forma automática que o spring boot tente configurar todas as dependências “jar” adicionadas na aplicação, como também inicializar as classes que estão no caminho da aplicação. De certo modo podemos pensar nela como uma espécie de varredura à procura de beans e adição desses com base nas configurações de classpah, e caso não haja a especificação de alguns beans o próprio spring tentará configurar de forma automática o que há em memória, como é o caso de um banco de dados não configurado no programa e o spring usa o seu de memória.

@ComponentScan : Essa anotação é responsável por encontrar os @Controller da aplicação. Vale ressaltar que, o spring não requer nenhum layout de código específico para funcionar, porém, na construção do programa é de boa prática seguir recomendações importantes para o bom funcionamento e assim, evitar bugs na hora da inicialização da aplicação. Outro ponto relevante quanto aos pacotes é o seu nome de domínio que é necessário ser invertido ( por exemplo com.exemplo.projeto).

Algo tão importante como as anotações na classe main é o seu método gatilho o SpringApplication.run(), através da invocação desse método que todo o contexto da aplicação roda, vale ressaltar que, essa não é o única forma de inicializar uma aplicação spring porém, essa opção é a mais recomendada, já que, ela induz uma única classe de entrada de configuração e de forma consequente permite carregar as outras classes sob um efeito cascata. Tendo apenas um único ponto de partida definido a legibilidade do código se torna mais simples permitindo e facilidade para um melhor entendimento e manutenção.

Como já entendemos como é inicializado um projeto spring vamos agora entender a construção das APIs. É recomendável definir primeiramente quais serão as interações dos serviços web da aplicação; o serviço terá um GET, POST, UPDATE, DELETE, a solicitação deve retorna um status 200 ok com um JSON no corpo. Definido isso já existe o ponto de partida.

Crie uma classe ProjetoController no pacote com.exemplo.projeto.controller, lembrando que o pacote principal será definido no momento do projeto spring.

Duas anotações são importantes para a classe ProjetoController são as @RestController, @RequestMapping. A @RestController especifica para o spring que a requisição será serializada para ser jogada na resposta da requisição. Essa serialização pode ser por um json. @RequestMapping é a anotação que implementa parte específica da URL para um serviço web.

 

package com.exemplo.projeto.controller;

 

@RestController

@RequestMapping("/api/v1/people")

public class PersonController {

 

    @GetMapping

    public String getBook() {

        return "Api Test!";

    }

 

}

 

 

Com um controller finalizado já podemos testar nossa aplicação através da url que configuramos e o método marcado com a @GetMapping será invocado e o valor da String será apresentado na página. Só lembrando que a url test será http://localhost:8080/api/v1/people. Isso se sua aplicação estiver configurada para a porta 8080.

 

Com aplicação testada e rodando avante na construção da API. Agora passos importantes precisam ser criados e muito bem estruturados para o contexto spring. Como a intenção é a interação da aplicação com um banco de dados é necessário uma configuração de pacotes e classes para uma melhor interpretação e execução de nossas dependências anotadas. Crie um pacote com.exemplo.projeto.entity, aqui será colocado nossas entidades que terão sua relação com o banco. Nessa aplicação será realizado um domínio de cadastros e criados duas classes: Person, Phone.

 

package com.exemplo.projeto.entity;

 

@Data

@Builder

@NoArgsConstructor

@AllArgsConstructor

@Entity

public class Person {

 

    @Id

    @GeneratedValue(strategy = GenerationType.IDENTITY)

    private Long id;

 

    @Column(nullable = false)

    private String firstName;

 

    @Column(nullable = false)

    private String LastName;

 

    @Column(nullable = false, unique = true )

    private String cpf;

 

    

    private LocalDate birthDate;

 

    @OneToMany              

    private List<Phone> phone;

 

}

 

Na nossa entidade para evitar excesso de códigos usamos as anotações do Lombok que podemos eliminar pelo menos de forma explicita os getters,setters,hashCode e toString de cada atributo usando apenas @Data. Como nossa classe pode ser inicializada por construtores vazios ou não as anotações @NoArgsConstructor e AllArgsConstructor se encarregam de gerar para a classe. @Build facilita de forma automática que a classe seja instanciada com códigos como: Person.build().firstName( “Name” ).lastName(“lastName”)... build(); e assim para cada campo da classe. É necessário terminar o preenchimento com o build().

 

Para tornar nossa classe algo visível para o nosso JPA, que logo será abordado, é necessário dizer que nossa classe é uma entidade e uma simples anotação @Entity já diz ao JPA que se trata de uma entidade. Outras três anotações importantes para configurar nossa classe como uma entidade seria @Id ,  o @GeneratedValue( strategy = GenerationType.IDENTITY) que irá ser vista pelo o banco como auto incremento e de caráter único como uma primary key e sempre que instanciado essa classe é gerado de forma automática no banco o Id. E a última @Column mostra que se trata de uma coluna referente a uma idêntica no banco de dados. Não entraremos em detalhes de alguns recursos dentro das anotações como é o caso da @OneToMany, para efeito didático essa anotação mostra o relacionamento entre as classes que possua vez essa relação também existirá no banco através das foreing key. Vale ressaltar que, com essa relação entre tabelas o banco H2 cria uma tabela memória dessa relação descrita como tabela Person_Phone.

Crie uma classe Phone no pacote entity.

package com.exemplo.projeto.entity;

 

@Data

@Builder

@NoArgsConstructor

@AllArgsConstructor

@Entity

public class Phone {

    

    @Id

    @GeneratedValue(strategy = GenerationType.IDENTITY)

    private Long id;

    

    @Enumerated(EnumType.STRING)

    @Column(nullable = false)

    private PhoneType type;

    

    @Column(nullable = false)

    private String number;

 

}

 

 

Nota-se que um dos atributos dessa nossa classe vem de outro pacote; esse pacote se chama com.exemplo.projeto.enums. Pois bem como uma pessoa pode possuir distintos tipos de telefones como: comercial, mobile ou home é interessante utilizar Enums para especificar qual é o tipo de telefone sem instanciar objetos na hora de dizer o tipo. Crie uma Enum no pacote especificado abaixo:

package com.exemplo.projeto.enums;

@Getter

@AllArgsConstructor

public enum PhoneType {

    Home("Home"), MOBILE("Mobile"), COMERCIAL("Comercial");

 

    private final String description;  

}

Até aqui já temos entidades criadas com suas respectivas anotações, relação entre as classes realizadas, anotações de mapeamento e comunicação com o banco feito e a interação com o banco ainda será feita. Hora de evoluir o código para um patamar de padrão de projeto.

Primeiramente criaremos nossos DTO (Objetos de Transferência De Dados) como forma de evitar a transparências dos atributos de nossas entidades. É recomendável a utilização desse recurso de “espelhamento” de classe, sendo que, podemos manipular atributos dos quais realmente interessem ao cliente e ao servidor. Vale ressaltar que, usaremos a especificação java bean validation (dependência já inclusa na hora de baixar o projeto, Validation) para o uso de anotações que permitem em tempo de execução a validação dos campos como caso da @NotEmpty que detecta se o campo está vazio.  Nesse contexto podemos imaginar que a classe Person terá um PersonDTO com todos atributos idênticos, com ressalva em alguns, e sendo assim, toda a interação de atributo para as Entity será intermediado pelo o DTO.

PersonDTO:

package com.exemplo.projeto.request.dto;

 

@Data

@Builder

@NoArgsConstructor

@AllArgsConstructor

public class PersonDTO {

 

    private Long id;

 

    @NotEmpty

    @Size(min = 2max = 100)

    private String firstName;

 

    @NotEmpty

    @Size(min = 2max = 100)

    private String LastName;

 

    @NotEmpty

    @CPF

    private String cpf;

 

    private String birthDate;

 

    @Valid

    @NotEmpty

    private List<PhoneDTO> phone;

 

}

 

PhoneDTO:

package com.exemplo.projeto.request.dto;

 

@Data

@Builder

@NoArgsConstructor

@AllArgsConstructor

public class PhoneDTO {

     

    private Long id;

    

    @Enumerated(EnumType.STRING)

    private PhoneType type;

    

    @NotEmpty

    @Size(min = 13max = 14)

    private String number;

 

}

 

 

Percebe-se que tanto a classe Person e PersoDTO possuem os mesmos atributos com exceção no tipo do atributo birthDate, que na classe Person é do tipo LocalDate e na classe PersonDTO é do tipo String, isso se da ao fato que na passagem do nosso json o corpo virá em String. O interessante é que a comunicação agora será feita através do DTO. Nosso controler terá objetos do tipo DTO, porém, o JPA recebe um tipo Person. Exemplificando, quando o controller passar para o service o objeto vindo da requisição na classe PersonService irá receber o objeto do tipo PersonDto o problema que os métodos do JPA espera um do tipo Person (Entidade). A conversão de objeto DTO para o tipo entidade renderia muitas linhas de códigos. Para evitar iremos usar um simplificador de mapeamento de DTOs em java que é o framework MapStruct. Ele pode ser baixado tanto para o maven ou gradle. Embora exista outras formas de mapeamento DTO iremos aplicar o MapStruct.

Vale ressaltar aqui alguns ajustes a serem feitos no pom.xml quanto a dependência :

  <dependency>

            <groupId>org.mapstruct</groupId>

            <artifactId>mapstruct</artifactId>

            <version>1.4.2.Final</version>

        </dependency>

 

 

 

 

No build :

      

    <path>

        <groupId>org.projectlombok</groupId>

            <artifactId>lombok</artifactId>

                <version>${lombok.version}</version>

    </path>

        <path>

            <groupId>org.mapstruct</groupId>

                <artifactId>mapstruct-processor</artifactId>

                    <version>1.4.2.Final</version>

        </path>

 

Terminado as configurações no pom.xml. Crie pacote mapper e uma interface PersonMapper.

package com.exemplo.projeto.mapper;

 

@Mapper

public interface PersonMapper {

    

 PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class);

 

    @Mapping(target = "birthDate",

     source = "birthDate"

     dateFormat = "dd-MM-yyyy")

    Person toModel(PersonDTO personDTO);

 

    PersonDTO toDTO(Person person);

 

 

Ela será uma interface e vamos entender o código:

@Mapper: indica ao MapStruct que a interface anotada será responsável pela declaração dos mapeamentos.

@Mapping: com essa anotação é possível interligar campos de diferentes classes pelo target e source, ainda que os nomes dos atributos fossem diferentes essa anotação iria fazer o pareamento. Percebe que a relação de mapeamento se dá de forma bem simples, quando quer ter acesso aos métodos do JPA o método é toModel, quando se quer passar para o cliente a resposta do banco toDTO.

Person toModel(PersonDTO personDTO);

 PersonDTO toDTO(Person person);

Acima é percebido que a forma de mapeamento se dará entre as classes Person e PersonDTO.

Para efeito organizacional vamos criar um DTO response e nele iremos apenas determinar um comportamento de um método de mensagens. Crie o pacote com.exemplo.projeto.response.dto nele crie a classe MessageResponse.

Package com.exemplo.projeto.response.dto;

 

import lombok.Builder;

import lombok.Data;

 

@Data

@Builder

public class MessageResponseDTO {

    

    private String message;

 

}

 

 

Nele temos apenas um atributo que iremos acessar através da anotação @Data.

Dando continuidade à criação da API vamos agora criar nosso gerenciador de comunicação com o banco de dados. Falando assim parece ser algo bem complexo, porém, não é. Antes até poderia ser, mas com a utilização JPA, toda comunicação e gerência entre a aplicação e o banco de dados é realizado por ela. Crie uma interface PersonRepository.

package com.exemplo.projeto.repository;

 

 

import org.springframework.data.jpa.repository.JpaRepository;

import one.digitalinnovation.personapi.entity.Person;

 

public interface PersonRepository extends JpaRepository<Person, Long> {

 

}

 

Pronto criado o repositório, bem simples. Agora todo processo de gerenciamento com o banco de dados será feito através dos métodos já existentes dessa interface. Mas apenas criar a interface de gerenciamento não é o bastante, é necessário alguém para coordenar nossa interface, de forma sinônima, podemos substituir coordenar por controle. Agora podemos pensar de forma padronizada dentro do universo spring e MVC, então iremos criar nossos controles, pois, através deles é implementados nossos serviços web de forma ordeira e legível. Vá para o pacote controller e faremos algumas alterações cruciais. Ele deve ficar assim:

package com.exemplo.projeto.controller;

 

@RestController

@RequestMapping("/api/v1/people")

public class PersonController {

 

    private PersonRepository personRepository;

 

    @Autowired

    public PersonController(PersonRepository personRepository) {

 

        this.personRepository = personRepository;

    }

 

    @PostMapping

    public MessageResponseDTO createPerson(@RequestBody Person person) {

        Person savedPerson = personRepository.save(person);

        return MessageResponseDTO

        .builder()

        .message("Created person with Id" + person.getId())

        .build();

    }

 

}

 

 

Entendendo o código.

    private PersonRepository personRepository;

Estamos instanciando nosso tipo de pessoa repositório.

 

    @Autowired

    public PersonController(PersonRepository personRepository) {

 

        this.personRepository = personRepository;

    }

Acima percebe um dos mais notáveis recursos do spring boot, as injeções de dependências. No contexto acima a anotação @Autowired gera dependência do meu repository com o meu controller e ainda permite o acesso a métodos do JPA sem necessariamente ficar gerando objetos com os famosos News.

 

    @PostMapping

    public MessageResponseDTO createPerson(@RequestBody Person person) {

        Person savedPerson = personRepository.save(person);

        return MessageResponseDTO

        .builder()

        .message("Created person with Id" + person.getId())

        .build();

    }

 

Nosso serviço Post definido através da anotação @PostMapping. Estamos salvando uma pessoa e dentro do corpo do método dizemos que será através de uma requisição de corpo pela anotação @RequestBory e dizemos o tipo que será person do tipo Person.

Nota-se que usamos um método da nossa JPA para salvar nosso corpo da aplicação. Como retorno chamamos a classe MessageResponseDTO apenas para nos indicar que foi salvo no banco, veja o poder da anotação @Build por ela podemos passar os valores de forma direta aos atributos da classe.

Pois bem rode a aplicação e assim já iremos criar as tabelas no banco H2 em memória apenas com as anotações JPA. Algumas observações são importantes ao subir aplicação existirá no console a url de requisição ao banco h2 e a url de acesso ao banco.

url: http://localhost:8080/h2console

url de acesso ao banco : gerada na hora que sobe a aplicação.

Para uma melhor estruturação e organização da aplicação vamos organizar nossos pacotes com direcionamento padrão MVC. Percebe-se que nosso pacote controller está com total reponsabilidade da regra do negócio sendo que é interessante e recomendável deixá-lo apenas com a função intermediário banco e aplicação. Nossa regra de negócio será dedicada ao um pacote nomeado service. Será através dele que nossos serviços web serão descritos. Crie uma pacote com.exemplo.projeto.service e dentro desse pacote crie uma classe PersonService.

 

 

 

 

 

 

 

 

package com.exemplo.projeto.service;

 

@Service

public class PersonService {

 

    private PersonRepository personRepository;

 

    private final PersonMapper personMapper = PersonMapper.INSTANCE;

 

    @Autowired

    public PersonService(PersonRepository personRepository) {

 

        this.personRepository = personRepository;

    }

 

    public MessageResponseDTO createPerson(PersonDTO personDto) {

        

        Person personToSave = personMapper.toModel(personDto);

        

        Person savedPerson = personRepository.save(personToSave);

        return MessageResponseDTO.builder()

                .message("Created person with Id" + savedPerson.getId()).build();

    }

}

 

 Nesta classe uma anotação é extremamente importante.

@Service: mostra que a lógica do negócio parte dessa classe.

Entendendo o código.

    private PersonRepository personRepository;

 

    private final PersonMapper personMapper = PersonMapper.INSTANCE;

 

    @Autowired

    public PersonService(PersonRepository personRepository) {

 

        this.personRepository = personRepository;

    }

 

Como toda a nossa regra de negócio será agora realizada por essa classe então faz todo sentido que nossa relação com o banco parta dessa classe. Sendo assim, o contrato com a interface JPA será implementado em nossa regra de serviço. Vale ressaltar que, a injeção de dependência será acentuada pela a anotação @Autowired. Nota-se também que invocamos a interface PersonMapper para a conversão de nosso DTO para um tipo Person, pois é como a JPA recebe.

    public MessageResponseDTO createPerson(PersonDTO personDto) {

        

        Person personToSave = personMapper.toModel(personDto);

        

        Person savedPerson = personRepository.save(personToSave);

        return MessageResponseDTO.builder()

                .message("Created person with Id" + savedPerson.getId()).build();

    }

}

 

Esse método é do tipo MessageResponseDTO nossa intenção é passar para ele os campos preenchidos do tipo PersonDTO vindos do controller , e através do método do JPA save() passar o objeto do tipo Person através da conversão do método toModel da interface PersonMapper. O retorno do método lança uma mensagem posterior o momento que é salvo o Post.

Reestruturando a classe Controller.

package com.exemplo.projeto.controller;

 

@RestController

@RequestMapping("/api/v1/people")

public class PersonController {

 

    private PersonService personService;

 

    @Autowired

    public PersonController(PersonService personService) {

 

        this.personService = personService;

    }

 

    @PostMapping

    @ResponseStatus(code = HttpStatus.CREATED)

    public MessageResponseDTO createPerson(@RequestBody @Valid PersonDTO personDto) {

        return personService.createPerson(personDto);

    }

}

 

Nossa classe PersonController terá um papel de intermediário entre o recebimento do Json vindo através da @RequestBody e da regra do negócio PersonService. Percebe que vem através da requisição http um objeto do tipo json e é passado para a nossa classe PersonService e salvado o objeto dentro do banco. O end point da Api pode ser testada tanto pelo Postman ou Insomnia. Criando seu Json do tipo Post e enviando à seguinte url: http://localhost:8080/api/v1/people.

 

 

Json:

    "firstName": "joão",

    "lastName": "freitas",

    "cpf": "021.100.670-11",

    "phone": [{

      "type": "MOBILE",

        "number": "(85)961789056"   

}]

    

}

 

@ResponseStatus(code = HttpStatus.CREATE) : Retorna o tipo de status da requisição tipo 201 para uma requisição de sucesso.

Outros tipos de serviços web serão definidos nas classes PersonService e PersonController. Urls de acesso pelo controller, regras de negócio pelo os serviços.

@Valid : Essa anotação valida cada campo que chega ao controller.

Toda a aplicação está em estado definido e organizado e o que resta agora é apenas implementar os serviços web restantes da API.

Vamos acrescentar um novo método no PersonController :

package com.exemplo.projeto.controller;

 

 

// código acima

    

    @GetMapping

    public List<PersonDTO> listAll(){

        return personService.listAll();

    }

    

 

Para esse novo método será instanciado do banco todos os registros feitos de objetos do tipo Person. Como se trata de uma busca de vários objetos precisamos de uma lista através de List<PersonDTO>, mas uma vez reitero que nossa classe controle não possui a regra de negócio da aplicação. Nossa anotation @GetMapping é uma reference do tipo de serviço web desse método.

 

 

 

Vamos acrescentar um novo método no PersonService:

package com.exemplo.projeto.service;

 

// código acima

    

    public List<PersonDTO> listAll() {

        List<Person> allPeople = personRepository.findAll();

 

        return allPeople.stream().

                map(personMapper::toDTO).

                collect(Collectors.toList());

 }

 

O serviço tem a reponsabilidade das consultas com repositório que é do tipo Person, sendo assim precisamos apenas colocar os objetos dentro de uma lista. Nosso findAll da nossa JPA se encarrega dessa busca e devolve ao atributo allPeople, até aqui tudo bem, porém nosso retorno ao método listAll da classe PersonControler precisa ser do tipo DTO. Sendo assim vamos entender nosso processo de conversão da nossa List<Person> para uma List<PersonDTO>. A partir do java 8 tivemos uma Api que permite manipulação de objetos do tipo lista.

Entendendo o código :

stream( ) : É uma Api do java para a manipulação e transformação de dados em coleções.

map( ) : Para cada dado do fluxo da coleção allPeople trará resultados de fluxo do tipo PersonDTO através do método toDTO da intefarce PersonMapping.

collect(Collectors.toList( )) : toda a lista é colocada dentro desse método.

 

 

 

 

Retorno de um objeto unicamente pelo o id, na classe PersonController acrescente o seguinte método:

package com.exemplo.projeto.controller;

 

// código acima

 

@GetMapping("/{id}")

    public PersonDTO findById(@PathVariable Long id)

     throws PersonNotFoundExcepition {

        return personService.findById(id);

    }

 

 

Nesse novo método a intenção é uma busca e retorno de um único objeto específico, porém algumas tratativas são importantes de serem levadas em consideração. A primeira delas é nossa anotação @GetMapping(“/{id}”) nota-se que é imprescindível o uso do objeto {id} no corpo da requisição, pois é necessário a passagem do identificador do objeto que se quer buscar. Em consequência disso precisamos considerar falhas esperadas na aplicação, ou seja, a passagem de um {id} inexistente no banco, iria acarretar no lançamento de uma excepition para evitar isso vamos criar uma tratativa dentro do próprio controle com nossa classe PersonNotFoundException, que até o momento ainda não foi criada. Para finalizar nosso controle retorna com uma chamada para o método dentro do serviço chamado findById.

Criando no serviço uma busca por Id :

package com.exemplo.projeto.service;

 

// código acima

 

public PersonDTO findById(Long id) throws PersonNotFoundExcepition {

 

    Optional<Person> optionalPerson = personRepository.findById(id);

 

    if(optionalPerson.isEmpty()) {

        throw new PersonNotFoundExcepition(id); 

        

    }        

        return personMapper.toDTO(optionalPerson.get());

    }

 

Nossa Exception é lançada em tempo de execução do método. O findById da JPA retorna um objeto do tipo Optional e esse precisa ser do tipo Person. Uma das vantagem de se usar Optional é que ele evita verificações nulas. Veja como é possível tratar através do Optional se o objeto buscado é vazio ou não através do isEmpty, e assim lançar nosso tratamento de erro. Como é preciso retornar para o controle um objeto do tipo DTO utilizamos um método da nossa PersonMapper chamado toDTO e dentro dele nosso objeto optionalPerson.get( ).

Criando a classe do tratamento de erros:

package com.exemplo.projeto.exception;

 

import org.springframework.http.HttpStatus;

import org.springframework.web.bind.annotation.ResponseStatus;

 

@ResponseStatus(code = HttpStatus.NOT_FOUND)

public class PersonNotFoundExcepition extends Exception {

    

    private static final long serialVersionUID = 1L;

 

    public PersonNotFoundExcepition(Long id) {

        super("Pessoa não encontrada com esse: "+ id);

    }

 

}

 

 

 

Acrescentando o update:

package com.exemplo.projeto.controller;

 

// código acima

 

@PutMapping("/{id}")

    public MessageResponseDTO updateById(@PathVariable Long id, 

     @RequestBody @Valid PersonDTO personDto) throws PersonNotFoundExcepition {

        

        return personService.updateById(id , personDto);

    }

    

 

Classe PersonController:

Para o nosso update será necessário dois tipos de entrada Long id referente à chave primária da entidade a ser atualizada e um objeto do tipo PersonDTO.

@PutMapping : Tipo de serviço o Put representa um update.

@PathVariable : A identificação de que se trata de uma chave primária e extração da parte modelada na URL do id.

@RequestBody: Há um corpo vindo da requisição.

Classe PersonService :

package com.exemplo.projeto.service;

 

// código acima

public MessageResponseDTO updateById(Long id, @Valid PersonDTO personDto)

 

 throws PersonNotFoundExcepition {

        

    Optional<Person> optionalPerson = personRepository.findById(id);

            

        if(optionalPerson.isEmpty()) {

                throw new PersonNotFoundExcepition(id);

            } 

        

Person personToUpdate = personMapper.toModel(personDto);

 

Person updatePerson = personRepository.save(personToUpdate);

    return MessageResponseDTO.builder().

        message("Update person with Id" + updatePerson.getId()).build();

            

    }

 

O método atualizar na classe PersonService é idêntico ao de salvar, porém no controle existe um parâmetro que representa a ação do método que é o id, se id recebido é inexistente no banco uma exception é lançada. Essa é única diferença entre o método salvar pois no corpo da requisição no controle não há necessidade de autenticação de id.

E por último vamos implementar o Delete para finalizarmos nossa Api-Rest.

Classe PersonController :

package com.exemplo.projeto.controller;

 

// código acima

    

    @DeleteMapping("/{id}")

    @ResponseStatus(code = HttpStatus.NO_CONTENT)

    public void deleteById(@PathVariable Long id)

     throws PersonNotFoundExcepition {

            

            personService.delete(id);   

    }

 

@DeleteMapping : Representando nosso serviço web como delete.

@ResponseStatus : Caso o id informado não exista no banco.

Classe PersonService :

package com.exemplo.projeto.service;

 

// código acima

    

public void delete(Long id) throws PersonNotFoundExcepition {

        

        Optional<Person> personById = personRepository.findById(id);

        

        if(personById.isEmpty()) {

            

            throw new PersonNotFoundExcepition(id);

        } else {

            personRepository.deleteById(id);

        }

        

    

 

Para o serviço apenas verificação se o objeto está vazio no banco e caso não o nosso personRepository.deleteById deleta do banco.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


0
0

Comentários (0)

Determinação faz parte da minha rotina diária.

Brasil