Article image
Wesley Corrêa
Wesley Corrêa23/01/2024 17:28
Compartilhe

Aplicações Backend em Java -Desenvolvendo CRUD de forma simples"

  • #Spring
  • #PostgreSQL
  • #Java

Neste Artigo, estaremos desenvolvendo uma aplicação do "zero" em linguagem Java, e tendo como objetivo principal fazer um “CRUD” de forma mais simples e explicativa possível, porém com uma arquitetura e código limpo.

Fundamentos de um CRUD

Primeiramente, o que é um CRUD? Explicando de maneira simplificada, um CRUD é basicamente as quatro operações básicas que uma aplicação deve-se fazer em um sistema com gerenciamento de banco de dados, essas quatro operações são:

  • Create: Com o objetivo de criar e armazenar novos registros nesse banco de dados.
  • Read: Operação responsável por recuperar e consultar as informações existentes no banco.
  • Update: Responsável por atualizar ou modificar os dados já existentes no banco de dados.
  • Delete: Refere-se em deletar registros existentes no banco de dados.

Ambiente de desenvolvimento

Para desenvolvermos essa aplicação, serão necessárias algumas ferramentas instaladas em sua máquina, aqui está uma lista detalhada do que será necessário:

  • Java JDK17 instalado
  • IDE compatível com Java (Exemplos com a IDE IntelJ)
  • Docker Desktop instalado
  • Gerenciador de Banco de dados instalado (Exemplos com DBeaver)
  • Postaman (Para testes dos "endpoints")

Implementação do projeto

Para iniciarmos esse projeto, utilizaremos uma ferramenta online que facilita a iniciação de novos projetos de forma simples e rápida. Acessando o site https://start.spring.io/ utilizaremos as configurações de projeto Maven, com linguagem Java e utilizando a versão do Spring Boot em 3.2.1 para esse projeto, além de preenchermos os "Metadata" conforme necessidades.

image

Figura 1 - Início do projeto

Além dessas informações, iremos adicionar ao lado direito, as dependências que serão usadas nesse projeto:

  • Spring Web
  • Spring Data JPA
  • Lombok
  • PostgresSQL Drive

Dessa forma:

image

Figura 2 - Dependências do projeto

Clicando em "Generate", logo abaixo, podemos fazer o download do “esboço” do projeto onde iremos criar a aplicação. Após isso, descompactar o arquivo, e abrir a pasta na IDE que será utilizada, nesse caso, na IDE IntelJ, e ficar dessa forma:

image

Figura 3 - Projeto iniciado

Agora, com o projeto iniciado, utilizaremos uma estrutura MVC, portanto será necessário criar seis package dentro da pasta do projeto, essas pastas serão:

  • Model: Onde ficarão todas as nossas Entidades.
  • DTO: Nesse package definimos as DTOs das entidades.
  • Controllers: Onde ficaram as classes responsáveis pelas rotas.
  • Services: Aqui ficam as classes responsáveis por fazer a regra de negócios dos nossos objetos.
  • Repositories: As interfaces que irão acessar o banco de dados com o JPA.
  • Config: Um packege de configurações para componentes utilizados no projeto

O projeto ficara com uma arquitetura bem distribuída e funcional, da forma que está separado, o projeto deve ficar dessa forma:

image

Figura 4 - Arquitetura do projeto

Iniciamos criando uma classe no package Model, com nome de People, essa classe será uma entidade do objeto People, esse objeto irá ter os atributos, ID, name, lastName e age. Além disso utilizaremos nessa classe quatro ‘anotations’ essenciais para o funcionamento da aplicação:

  • @Data – Uma anotação da biblioteca do Lomboak que foi adicionada no inicio do projeto, tem como objetivo eliminar a necessidade de escrever os métodos getters, setters, toString, equals, e hashCode. Essa anotação adiciona esses métodos automaticamente durante a compilação do código, tornando mais limpo.
  • @Entity – Uma anotação do JPA, outra dependência utilizada no inicio do projeto, tem como objetivo definir essa classe como uma entidade persistente, ou seja, um objeto que será persistido no banco de dados relacional.
  • @Tabla – Essa anotação é usada em conjunto com a @Entity para adicionar detalhes sobre a tabela associada a esse objeto no banco de dados, nomeando o nome da tabela onde esse objeto será persistido.
  • JsonInclude – Essa anotação ajuda a controlar a inclusão/exclusão de propriedades nulas ou vazias do JSON relacionado a essa entidade.

Depois de criar a Entidade/Classe People, teremos uma classe dessa forma:

image

Figura 5 - Classe ou Entidade People

No “package” DTO, teremos a classe PeopleDTO, que é muito comum em muitos projetos, esse DTO, refere-se ao “Data Transfer Object”, que por boas praticas utiliza-se basicamente para separar a representação interna dos objetos com o que está sendo transferido com outras classes e até mesmo banco de dados.

Essa classe, basicamente uma “cópia” da entidade, porém pode-se restringir a quais atributos você vai transferir, por exemplo, se eu não posso fornecer todos os dados daquela entidade para um certo método ou serviço, eu faço uma classe DTO apenas com os atributos que eu gostaria que fosse transferido, basicamente é isso!

No caso desse projeto, vou utilizar a DTO por boa pratica, pois minha DTO estará idêntica a minha Entidade, ou Model, como preferir. Essa classe terá a ‘anotation’ @Data, apenas ela, que tem como objetivo deixar o código mais limpo, e mais fácil de se ler.

Segue o exemplo de como ficará a classe PeopleDTO:

image

Figura 6 - Classe PeopleDTO

Agora antes de começarmos os packages Controllers, Services e Repositories, deve-se fazer as configurações do projeto. No package Config, cria-se uma classe chamada Config e nela utilizaremos a anotation @Configuration, para indicar para nossa aplicação essa classe é uma classe de configuração.

Nessa classe, criaremos uma Bean para iniciarmos o ModelMapper, para auxiliar na transição de um tipo de objeto para outro, como veremos mais à frente.

Exemplo da classe “Config” com o método criado (Bean):

image

Figura 7 - Classe de configuração

Enfim com as configurações criadas, o próximo passo, será implementar a interface no package Repositories. Basicamente essa interface irá “conversar” com o banco de dados, e essa única interface será o suficiente para atender todos os métodos que estarão no package Service. Essa interface deverá ser dessa forma:

image

Figura 8 - Interface na camada Repository

Agora, com a interface pronta, vamos fazer uma abordagem diferente, sabe-se que quando fazermos uma chamada na "Api", essa chamada vai chegar diretamente na Controller, e esse método, fará a regra de negócio do programa, na camada Service.

A Controller terá básicos cinco métodos:

  • findAll – Responsável por buscar uma lista com todos os dados da tabela salva no banco de dados.
  • findById – Método que buscará um objeto “pessoa” a partir de um id especifico.
  • Insert – Método que tem como objetivo salvar o objeto no banco de dados.
  • Update – Responsavel por atualizar um objeto já existente no banco de dados.
  • Delete – Método que deletará um objeto no banco de dados com ID especifico.

Na camada “Controller”, haverá uma classe chamada PeopleController, e na camada “Service”, uma classe chamada PeopleService, ficando as duas classes respectivamente dessa forma:

image

Figura 9 - Classe PeopleController

image

Figura 10 - Classe PeopleService

Tentando ser um pouco mais especifico, farei uma breve explicação de cada método na camada “Controller“, e ao mesmo tempo tentando explicar a continuação dele na camada “Service”, começando pelos métodos FindAll e FindByID:

  • FindAll:

image

Figura 11 - Controller - Método FindAll

Após chegar à requisição na camada “controller” onde é chamado esse método, é criado uma Lista de objetos PessoaDTO e essa lista é preenchida ao chamar o método findAll na camada “Service, que estará dessa forma:

image

Figura 12 - Service - Método FindAll

Esse método chama a interface da “repository” e então recebe uma lista de um objeto People, que logo em seguida, transforma esse objeto em PeopleDTO e retorna esse objeto para a “controller”. Após isso, o método na “controller” retorna para a requisição uma lista desse objeto PeopleDTO.

  • FindByID:

image

Figura 13 - Controller - Método FindByID

Esse método, tem o mesmo principio básico que o método “findAll”, porém é passado um “argumento ID” na rota {id}, com esse ID, o busca-se o método na “service”.

image

Figura 14 - Service - Método FindByID

No método findByID, dentro da camada “service”, o método busca novamente a interface de camada “repository”, e retorna para a camada “controller” um único objeto PeopleDTO com ID específico. E por fim, o método retorna um Objeto PeopleDTO na requisição.

  • Insert:

O método “insert”, já não é um método GET, e sim um método POST, esse método recebe um Body da requisição, onde deve-se conter um JSON, representando um objeto PeopleDTO. Ao receber esse “body”, o método envia-o como argumento para outro método na “service”, ficando dessa forma:

image

Figura 15 - Controller - Método Insert

No método da “service” (Método abaixo), ele transforma esse objeto em um objeto People e passa como argumento para a interface onde vai salvar esse objeto no banco de dados, e depois disso retorna para a camada “controller” o objeto que foi persistido no banco, dessa forma como segue o exemplo:

image

Figura 16 - Service - Método Insert

Finalizando essa requisição, o método na camada “controller” retorna o objeto persistido.

  • Update:

Esse é o método do tipo PUT, e responsável por atualizar um objeto já existente, portanto, o JSON que deverá ser enviado no body, deve-se conter o ID do objeto salvo no banco para atualizar o mesmo.

Esse método deve-se seguir com os mesmos princípios dos outros, recebe a requisição na controller, chama a service para fazer as devidas conversões, passa para a repository para acessar ao banco e depois devolve o objeto conforme as necessidades de cada método. Segue o exemplo na camada “controller”:

image

Figura 17 - Controller - Método Update

Como já explicado, o método acessa a camada “service”, ficando dessa forma:

image

Figura 18 - Service - Método Update

Nessa camada “service”, como já explicado, antes de mais nada, busca-se o objeto apartir do método this.findById(id), passando o ID do body como argumento, e logo após, transforma-se esse objeto PeopleDTO em objeto People, para poder enviar para o método this.insert(peoplePersisted) e salvar no banco de dados. E depois disso segue o fluxo enviando novamente para a camada “controller” e finalizando a requisição.

  • Delete:

O último método, é o mais “simples”, basicamente a recebe-se da rota um ID, e esse ID é passado como argumento na camada para o método delete() na camada “service”, dessa forma:

image

Figura 19 - Controller - Método Delete

Na camada “service”, é apenas chamado a “repository”, com argumento ID novamente, e a interface é responsável em deletar o objeto com o id específico.

image

Figura 20 - Service - Método Delete

Apenas um detalhe, método na camada “service” é um método void, portanto, não há retorno, e na camada “controller”, retorna um ResponseEntity<Void>.

Com todos os métodos prontos, vamos ter que fazer algumas configurações no arquivo “application.properties” do projeto, nesse arquivo, será definido várias propriedades do projeto, e deverá estar dessa maneira:

image

Figura 21 - Código no application.properties

Descrevendo superficialmente o que cada linha é responsável, obtemos isso:

  • server.port=8090 – Significa que a aplicação utilizara a porta 8090.
  • spring.datasource.url=jdbc:postgresql://localhost:5432/postgres-database – A “url” de conecção com o banco de dados que será utilizado.
  • spring.datasource.username=postgres – O nome de usuário para conectar ao banco de dados.
  • spring.datasource.password=postgres – O password para conectar do banco.
  • spring.datasource.driverClassName=org.postgresql.Driver – Especifica a classe do drive JDBC para conectar com o banco de dados.
  • spring.sql.init.platform=postgresql – Especifica a plataforma utilizada para os ”scripts” de inicialização SQL.
  • spring.sql.init.mode=Always – Essa linha garante que toda inicialização da aplicação os “scripts” serão executados.
  • spring.jpa.hibernate.ddl-auto=update – Garante que o “Hibernate” sempre atualizem os “schemas” no banco de dados com base nas classes e entidades do projeto.

Com esse código, é possível fazer alguns exemplos para “testarmos” essa aplicação.

Implementando o banco de dados

Com o Docker Desktop instalado, utilizaremos um comando no terminal para criar um container a partir de uma imagem do banco de dados PostgreSQL, e utilizarmos esse container para salvar os dados do projeto. Esse comando, irá gerar esse container e definirá as variáveis existentes, ficando dessa forma:

docker run -d --name database-postgres -e POSTGRES_DB=postgres-database -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 postgres

Utilizando esse comando, o contêiner com banco postgres será criado, e com as variáveis passadas no ”docker run” ficará dessa forma:

  • Nome do container: “database-postgres”,
  • Nome do banco de dados: “postgres-database”
  • Nome de usuário: “postgres”
  • Password: “postgres”
  • Porta do container mapeada para o host: “5432”

Com o banco de dados criado, pode-se iniciar a aplicação e a própria aplicação mapeara o banco de dados conforme a/as classes/entidades do projeto.

Para iniciar a aplicação, depois de tudo configurado, utilizando o “IntliJ” basta clicar no atalho “Run” ou utilizar o atalho “Shift + F10”, dessa forma:

image

Figura 22 - Iniciando a aplicação com IntliJ

Após conseguir rodar a aplicação, pode-se utilizar uma ferramenta de administração e desenvolvimento de banco de dados, nesse caso, os exemplos serão pelo DBeaver. Utilizando o DBeaver, pode-se acessar a tabela criada no banco, e deve estar dessa forma, pronta para uso:

image

Figura 23 - Diagrama no banco de dados

Com o Banco de dados “okay”, utilizaremos uma outra ferramenta, para auxiliar a fazermos testes na pratica, essa ferramenta chama-se Postman, e com ela, poderemos fazer chamadas em nossa aplicação para testar o funcionamento do CRUD.

O primeiro método a ser testado, será o método INSERT, pois ainda não temos nenhum dado no banco. Esse método, é um método POST, e com ele, enviaremos um JSON no body. Esse JSON nada mais é que a representação de um objeto “PeolpleDTO” na aplicação. Os JSONs utilizados serão todos parecidos com esse:

image

Figura 24 - Json teste para o método INSERT

Com esse JSON e com a aplicação funcionando, faremos três chamadas na aplicação, (Alterando o JSON, apenas para não salvar dados repetidos) que ficará dessa forma:

image

Figura 25 - Chamada no endpoint do método POST/Insert

Observe que para fazer a requisição, é solicitado um método POST, utilizando a o “endpoint” da aplicação conforme definimos na camada “controller”, passando um JSON representando um objeto “PeopleDTO”. Após essas três chamadas, pode-se verificar pelo DBeaver novamente, que os dados foram salvos e estão dessa forma:

image

Figura 26 - Dados salvos no banco de dados

Agora que o banco de dados já está “povoado”, podemos fazer os testes dos métodos findAll e findByID, nesse caso, os dois métodos, são métodos GET, e utilizaremos no POSTMAN para acessar esses métodos os “endpoints” http://localhost:8090/v1/people e http://localhost:8090/v1/people/1 lembrando que o numero no final dd “endpoint” do método findByID, é o ID que será buscado no banco de dados.

Com a aplicação ainda “rodando”, acessamos os “endpints” e obtemos os seguintes exemplos:

image

Figura 27 - Chamada no endpoint do método GET/findAll

image

Figura 28 - Chamada no endpoint do método GET/findByID

Observe que ao fazer essa requisição no “endpoint” findAll, é retornado uma lista de objetos People, como esperado, e no caso da requisição do “endpoint” findByID, retorna-se um objeto com ID que foi passado na própria “url”.

Para fazer o teste do método update, que pretendemos alterar algum campo do objeto no banco de dados, será um método do tipo PUT, que acessa o método Update, nessa requisição, é essencial, que seja passado um “body” contendo o ID e os demais campos do objeto onde será atualizado, dessa forma:

image

Figura 29 - Chamada no endpoint do método PUT/update

Ao realizar a chamada com o “body” conforme a figura acima, foi alterado no banco de dados o objeto com ID igual “2”, os campos conforme o JSON.

Para finalizar os devidos testes, façamos um ultimo teste, o método Delete, lembrando que é apenas para teste, pois esse método, deletará o objeto com ID de referência, e esse objeto não poderá ser recuperado após a exclusão.

Para esse método utilizaremos a “url” http://localhost:8090/v1/people/2 , portanto o objeto com ID “2”, será deletado. Um outro detalhe, é que como será deletado, a resposta da requição deverá ser vazia, contendo apenas o retorno do status “204” e “No Content” ficando da seguinte forma:

image

Figura 30 - Chamada no endpoint do método DELETE/delete

Este é o meu primeiro artigo e espero que tenha sido útil. Sou Wesley Silvestre Corrêa, e o que eu puder ajudar, estarei disponível, pois ainda estou em fase de aprendizado, busco e estudo muito para atingir meus objetivos, e sempre tive comigo, que quem ajuda o próximo, também está se ajudando, conhecimento se adquire compartilhando.

Escreverei outros artigos utilizando esse como base, melhorando o projeto e adicionando mais aprendizados e boas pratica, esse é apenas o inicio de um projeto que pretendo estender adicionando vários conhecimentos e exemplos.

Deixo aqui o repositório desse projeto para quais quer duvida e também os links para acessar o meu perfil no “github” e “linkedin”.

Repositório: https://github.com/WesleySCorrea/basic-crud-backend-java

Github: https://github.com

Linkdin: https://www.linkedin.com/in/wesleysilvestrecorrea

Compartilhe
Comentários (2)
Marcio Herrero
Marcio Herrero - 24/01/2024 11:29

Muito bom, o artigo ficou bem completo.

Parabéns!

Belisnalva Jesus
Belisnalva Jesus - 23/01/2024 21:26

Parabéns Wesley, Muito legal