Bom neste projeto vamos fazer uma API Restful usando o spring framework por intermédio de alguns de seus sub projetos, sera usado para este projeto as seguinte tecnologias
- spring boot 1.5.10
- Maven
- spring data JPA
- postgresql
- Java 8
- Postma para testar no API
- eclipse ( com plugin STS )
Este Projeto foi criado usando o eclipse
new -> Other -> Spring Starter Project
esta imagem mostra como estruturei o projeto dentro do eclipse
LivroRepository.java
fazendo o extends da interface JpaRepository do Spring temos ao nosso dispor varios metodos uteis para operações de CRUD como pode ser visto abaixo
pessoal na classe abaixo o extends JpaRepository deve esta assim JpaRepository<Livro, Long> que é a classe de entidade e o tipo da sua chave primaria.
package br.com.restful.repository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import br.com.restful.model.Livro; @Repository public interface LivroRepository extends JpaRepository { }
nesta classe injetamos a interface LivroRepository e encapsulamos os metodos de interação com o banco de dados a annotation @Component serve para torna esta classe gerenciada pelo spring outra particularidade e @Transactional(readOnly=true), este (readOnly=true) serve para informar qual metodo é apenas de leitura
LivroService.java
package br.com.restful.service; import java.io.Serializable; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import br.com.restful.model.Livro; import br.com.restful.repository.LivroRepository; @Component public class LivroService { @Autowired private LivroRepository livroRepository; @Transactional public Livro save(Livro entity) { return livroRepository.save(entity); } @Transactional(readOnly=true) public Livro getById(Serializable id) { return livroRepository.findOne((Long) id); } @Transactional(readOnly=true) public List getAll() { return livroRepository.findAll(); } @Transactional public void delete(Serializable id) { livroRepository.delete((Long) id); } }
esta é um simples POJO configurada com o JPA
Livro.java
package br.com.restful.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = "livro") public class Livro implements java.io.Serializable { private static final long serialVersionUID = 4910225916550731446L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", unique = true, nullable = false) private Long id; @Column(name = "isbn") private String isbn; @Column(name = "titulo") private String titulo; @Column(name = "descricao") private String descricao; @Column(name = "preco") private Integer preco; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getIsbn() { return isbn; } public void setIsbn(String isbn) { this.isbn = isbn; } public String getTitulo() { return titulo; } public void setTitulo(String titulo) { this.titulo = titulo; } public String getDescricao() { return descricao; } public void setDescricao(String descricao) { this.descricao = descricao; } public Integer getPreco() { return preco; } public void setPreco(Integer preco) { this.preco = preco; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((id == null) ? 0 : id.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Livro other = (Livro) obj; if (id == null) { if (other.id != null) return false; } else if (!id.equals(other.id)) return false; return true; } @Override public String toString() { return "Livro [id=" + id + ", isbn=" + isbn + ", titulo=" + titulo + ", descricao=" + descricao + ", preco=" + preco + "]"; } }
Esta é um simples POJO onde informações que serão mostradas quando acontecer alguma exception na nossa aplicação
DetalheErro.java
package br.com.restful.exception; public class DetalheErro { private int errorCode; private String detalhe; public int getErrorCode() { return errorCode; } public void setErrorCode(int errorCode) { this.errorCode = errorCode; } public String getDetalhe() { return detalhe; } public void setDetalhe(String detalhe) { this.detalhe = detalhe; } }
nesta classe criamos uma exception personalizada para ser lançada em algumas particularidades da nossa aplicação
RestException.java
package br.com.restful.exception; public class RestException extends Exception { private static final long serialVersionUID = -4852716389437651863L; private String errorMessage; public String getErrorMessage() { return errorMessage; } public RestException(String errorMessage) { super(errorMessage); this.errorMessage = errorMessage; } public RestException() { super(); } }
Nesta classe em cada metodo anotado com @ExceptionHandler(Exception.class) e possível de acordo com a exceção lancada montar um response tratado com todas as informações que quisermos enviar para quem estiver consumindo nossa api
RestExceptionHandler.java
package br.com.restful.exception; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; @ControllerAdvice public class RestExceptionHandler { @ExceptionHandler(RestException.class) public ResponseEntity restException(Exception ex) { DetalheErro error = new DetalheErro(); error.setErrorCode(HttpStatus.BAD_REQUEST.value()); error.setDetalhe(ex.getMessage()); return new ResponseEntity(error, HttpStatus.NOT_FOUND); } @ExceptionHandler(Exception.class) public ResponseEntity exceptionHandler(Exception ex) { DetalheErro error = new DetalheErro(); error.setErrorCode(HttpStatus.BAD_REQUEST.value()); error.setDetalhe("Um exceção foi lançada."); return new ResponseEntity(error, HttpStatus.BAD_REQUEST); } }
aqui no controller rest tentei fazer o mas auto explicativo possível e também seguindo algumas boas praticas de programação de api rest como quando adicionamos um novo recurso enviamos um HttpStatus.CREATED ao cliente ou entao quando buscamos um recurso com sucesso no banco retornamos um HttpStatus.OK
LivrariaController.java
package br.com.restful.controller; import java.util.Arrays; import java.util.List; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import br.com.restful.exception.RestException; import br.com.restful.model.Livro; import br.com.restful.service.LivroService; @RestController @RequestMapping("/livros") public class LivrariaController { final static Logger logger = Logger.getLogger(LivrariaController.class); @Autowired LivroService livroService; @RequestMapping(method = RequestMethod.POST) public ResponseEntity adicionarLivros(@RequestBody Livro livro) { livroService.save(livro); logger.debug("Livro adicionado:: " + livro); return new ResponseEntity(livro, HttpStatus.CREATED); } @RequestMapping(method = RequestMethod.PUT) public ResponseEntity atualizarLivros(@RequestBody Livro livro) throws RestException { Livro existingLiv = livroService.getById(livro.getId()); if (existingLiv == null) { throw new RestException("erro nao atualizacao de livro"); } else { livroService.save(livro); return new ResponseEntity(HttpStatus.OK); } } @RequestMapping(value = "/{id}", method = RequestMethod.GET) public ResponseEntity buscarLivro(@PathVariable("id") Long id) throws RestException { Livro livro = livroService.getById(id); if (livro == null) { throw new RestException("Livro com id "+id+" nao existe"); } logger.debug("Livro :: " + livro); return new ResponseEntity(livro, HttpStatus.OK); } @RequestMapping(method = RequestMethod.GET) public ResponseEntity buscarTodosLivros() { List livro = livroService.getAll(); if (livro.isEmpty()) { logger.debug("Nao existem livros cadastrados"); return new ResponseEntity(HttpStatus.NO_CONTENT); } logger.debug(Arrays.toString(livro.toArray())); return new ResponseEntity(livro, HttpStatus.OK); } @RequestMapping(value = "/{id}", method = RequestMethod.DELETE) public ResponseEntity apagarLivros(@PathVariable("id") Long id) throws RestException { Livro livro = livroService.getById(id); if (livro == null) { throw new RestException("Livro com id "+id+" nao existe"); } else { livroService.delete(id); logger.debug("Livro com id " + id + " deletado"); return new ResponseEntity(HttpStatus.GONE); } } }
Esta é a classe responsavel por subir nossa aplicação com spring boot
Application.java
package br.com.restful; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
Este arquivo de propriedades é usado pra configurar a aplicação em varios sentidos como a configuração do path da aplicação do banco de dados da jpa e do log
application.properties
# Configuracao do Servidor server.contextPath = /rest # Configuracao do banco de dados spring.datasource.driverClassName=org.postgresql.Driver spring.datasource.url=jdbc:postgresql://localhost:5432/postgres spring.datasource.username=postgres spring.datasource.password=postgresql # JPA spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true # Configuracao do log da aplicacao logging.level.com.bytestree.restful=DEBUG
Agora vamos testar a nossa aplicação
Adicionando:
Listando:
Atualizando:
Deletando:
Listando por Id:
Bom pessoal por hora é só. a ideia é evoluir este projeto em alguns post a mais incluindo talvez teste automatizado, integracao com swagger ou quem sabe criar um cliente para esta API em VueJs
Deixe um comentário