Repositório com códigos desenvolvidos no curso ESR da Algaworks.
Cada branch representa o código desenvolvido na aula referenciada. Algumas aulas são apenas teóricas, sem desenvolvimento de código.
Criação do projeto básico do Spring
Criação de um controller básico
Adição da dependência DevTools para reinicialização rápida durante o desenvolvimento da aplicação
Utilização da anotação @Component para definir beans gerenciados pelo Spring.
Exemplo com um simulador de notificação de emails para clientes
Injeção de dependências através de construtor Criação da interface Notificador (padrão de projeto Factory)
Criação de classes de configuração e beans
Injeçao de dependências por construtor, setter e anotação @Autowired
Configuração de dependência opcional com o parâmetro required na anotação @Autowired
Desambiguação de beans utilizando listas. Todos os beans são utilizados
Utilização da anotação @Primary para definir prioridade de beans
Utilização da anotação @Qualifier para definir prioridade de beans
@Qualifier é resolvido em tempo de execução.
Criação de anotação para definir prioridade do bean.
Criação de profiles com arquivos *.properties. Utilização da anotação @Profile
Configuração de callbacks de inicialização e finalização do bean com
- Anotações @PostConstruct e @PreDestroy
- Classe de configuração com propriedades init e destroy na anotação @Bean
Implementação do padrão de projeto Observer para notificações
Configuração do application.properties.
Link da documentação do Spring: https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html
Criação e utilização de propriedades no arquivo application.properties e utilização em NotificadorEmail com anotação @Value
Configuração da classe NotificadorProperties e utilização de propriedades com anotação @ConfigurationProperties
Configuração inicial do Spring Data JPA e das propriedades do banco de dados
Mapeamento das entidades Restaurante e Cozinha. Anotações @Entity, @Table, @Id e @Column.
Geração automática de tabelas utilizando as entidades e as configurações spring.jpa.generate-ddl e spring.jpa.hibernate.ddl-auto
Configuração do autoincrmento do id através da anotação @GenerationType
Criação de script para popular a tabela cozinha no banco de dados.
Arquivo com nome import.sql (obrigatório ser esse nome)
Listagem de objetos da classe Cozinha.
Uso da classe EntityManager com a anotação @PersistenceContext.
Ver SQL gerado com spring.jpa.show-sql
Inserção de dados no banco.
Método merge.
Necessário anotar o método adicionar com @Transactional
Busca de cozinha por Id.
Sem tratamento para Id inexistente.
Atualização de dados no banco.
Refatoração: Mudança do nome do método adicionar para salvar.
Exclusão de dados do banco.
Necessário obter instância gerenciável do objeto.
Artigo sobre JPA e estados da instância: https://blog.algaworks.com/tutorial-jpa/
Implementação de interface CozinhaRepository e da classe de implementação CozinhaRepository.
Cópia dos métodos de CadastroCozinha para CozinhaRepositoryImpl.
Exclusão da classe CadastroCozinha.
Refatoração das classes de manipulação de dados criadas nas aulas 3.9 a 3.12.
Configuração da dependência do Lombok.
Anotações @Getter, @Setter, @EqualsAndHashCode e @Data.
Propriedade onlyExplicityIncluded em @EqualsAndHashCode.
Utilização na classe Cozinha.
Utilização do Lombok na classe Restaurante.
Criação do repositório de restaurantes.
Inserção de SQL para restaurantes.
Configuração de relacionamento muitos pra um na entidade Restaurante.
Configuração do dialeto padrão do banco de dados com spring.jpa.hibernate.dialect (application.properties)
Definição de nome para a coluna de relacionamento com @JoinColumn com o parâmetro name
Configuração de propriedade não nula nas colunas (válido apenas para geração automática de tabelas)
Mapeamento de entidades e criação dos repositórios
- Cidade
- Estado
- Permissao
- FormaPagamento
Criação do controlador para obtenção de lista de cozinhas
Criação do controlador para obtenção de lista de estados
Definição de formato de dados para utilização nos endpoints (JSON ou XML).
O formato pode ser definido em escopo de classe (na anotação RequestMapping) ou em escopo do método (na anotação GetMapping, por exemplo).
A anotação de método tem prioridade sobre a anotação de classe.
O mesmo mapeamento pode produzir diferentes formatos.
Mapeamento para recurso único.
Utilização da anotação @PathVariable
Aula 4.15 - Customizando as representações XML e JSON com @JsonIgnore, @JsonProperty e @JsonRootName
- @JsonRootName - Mudar nome da representação da classe (XML)
- @JsonProperty - Pode ser utilizada para alterar a representação do at
- @JsonIgnore - Ignorar a propriedade na representação
@JsonProperty possui prioridade sobre @JsonIgnore. Caso ambas sejam colocadas no mesmo atributo @JsonProperty funcionará corretamente e @JsonIgnore será desconsiderada.
Customização da lista no XML. Criação da classe CozinhaXmlWrapper Anotações utilizadas:
- @JacksonXmlRootElement - define o nome do wrapper da lista
- @JacksonXmlProperty ou @JsonProperty - define o nome do recurso único
- @JacksonXmlElementWrapper - elimina a duplicação
Alteração do status de resposta da requisição
Configuração do status de retorno utilizando ResponseEntity
Lançamento de código 404 para recurso inexistente.
Inclusão de cozinha com POST em /cozinhas.
Alteração do status de resposta para 201 (Created).
Alteração de cozinha com PUT.
Tratamento de erro quando o Id não existe (status 404).
Exclusão de cozinha com DELETE.
- Em caso de sucesso, status 204 (No Content)
- Em caso de falha por não existir o recurso, status 404 (Not Found)
- Em caso de falha por violação de integridade de chave estrangeira, status 409 (Conflict)
Implementação da camada de serviços para Cozinha.
Anotação @Service.
Utilizar esta camada para implementação de regras de negócio.
Alteração do método de exclusão no repositório para receber o id como parâmetro. Lançamento de exceção EmptyResultDataAccess -> EntidadeNaoEncontradaException Lançamento de exceção DataIntegrityViolationException -> EntidadeEmUsoException
Retirada do suporte a XML Implementação do controlador de restaurantes
- Lista de restaurantes (/restaurantes)
- Busca de restaurantes (/restaurantes/{id})
Implementação de adição de restaurante (POST /restaurantes) Criação da classe de serviços de restaurante Tratamento de exceção para inserção de restaurante com cozinha inexistente
Implementação da atualização de restaurante (PUT /restaurantes/{id})
Implementação de serviços de estados e cidades
- Listagem (GET /estados - /cidades)
- Busca (GET /estados/{id} - /cidades/{id})
- Inclusão (POST /estados - /cidades)
- Atualização (PUT /estados/{id} - /cidades/{id})
- Exclusão (DELETE /estados/{id} - /cidades/{id})
Início da implementação de atualização parcial de restaurante (PATCH /restaurantes/{id}).
Utilização de mapa (Map)
Utilização da Reflections API para atualização parcial
- findField
- getField
- setField
Anotação @Repository
Consulta JPQL por nome de cozinha
Utilização de cláusula where
Exclusão da implementação de respositório de cozinha.
Interface de repositório anotada com @Repository e herança de JpaRepository.
Código não executa. Correções na próxima aula
Utilização dos métodos de JpaRepository para cozinhas
- findAll
- findById (retorna Optional)
- save
- deleteById
Remoção das implementações de reposítorio de Restaurante, Cidade, Estado, Permissao e FormaPagamento.
Refatoração dos serviços e controladores de Restaurante, Cidade e Estado.
Método com o nome da propriedade (teste com propriedade nome da classe Cozinha). Prefixo findBy
- Containing
- Between
- And Outras palavras chave: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation
- First - Retorna o primeiro resultado da lista
- Top - Retorna uma lista com os x primeiros elementos
- Exists - Verifica a existência de um elemento na lista que satisfaz um critério de seleção
- Count - contagem de elementos da lista que satisfazem um critério de seleção
Utilização de @Query para consultas Anotação @Param para compatibilizar nomes de variáveis
Criação de arquivo orm.xml para colocação da consulta JPQL (Anotação @Query pode ser retirada do método)
Implementação de métodos de forma customizada.
Padrão de nome da classe: Impl
Extração de interface customizada e herança múltipla de interface no repositório
Consulta dinâmica utilizando JPQL e Hashmap. Possibilidade de utilizar apenas alguns parâmetros
Introdução à Criteria API. Consulta simples para lista de restaurantes
Consultas não dinâmicas com Criteria API e cláusula where.
Necessário utilizar Predicates
Implementação de lista dinâmica de filtros com ArrayList de Predicate
Criação das specifications ComFreteGratis e ComNomeSemlhante.
Classes herdam de Specification (org.springframework.data.jpa.domain.Specification).
Ajuste do repositório para herdar a interface JpaSpecificationExecutor (org.springframework.data.jpa.repository.JpaSpecificationExecutor)
Fábrica de Specifications para restaurantes.
Exclusão das classes individuais referentes às specifications ComFreteGratis e ComNomeSemlhante
Mudança da chamada das specs para o repositório customizado.
Problema de referência circular do repositório - corrigido com anotação @Lazy
Criação do CustomJpaRepository para implementações comuns a várias classes.
Habilitação do novo repositório em AlgafoodApiApplication com anotação @EnableJpaRepositories(repositoryBaseClass = CustomJpaRepositoryImpl.class)
Implementação de relacionamento bidirecional entre restaurante e cozinha.
Anotação @OneToMany com propriedade mappedBy.
Correção de referência circular entre entidades com @JsonIgnore
Implementação de relacionamento muitos para muitos entre Restaurante e FormasPagamento.
Anotação @JoinTable com propriedades joinColumns e inverseJoinColumns
Mapeamento de classes embutidas. Exemplo com endereço e restaurante
Utilização de timestamps de criação e atualização de dados com anotações próprias do Hibernate.
Mapeamento da entidade Produto com relacionamento muitos-pra-um com a entidade Restaurante
Mapeamento do relacionamento um para muitos entre Restaurante e Produto.
Correção no controller de produto para evitar problemas na atualização dos dados.
Criação das entidades Grupo e Usuario.
Mapeamento muitos para muitos entre
- Usuario e Grupo
- Grupo e Permissao
Correção do problema N+1, que gera excesso de consultas SQL. Utilização do join fetch na consulta JPQL.
Configuração de mínimo e máximo de conexões e tempo ocioso (idle-timeout)
Migração para tabela cozinha.
Padrão: V__.sql
Migração da tabela cidade (sem foreign key para estado)
Migração da tabela estado. Ajuste da tabela cidade para receber a chave estrangeira de estado
Migração das tabelas
- restaurante
- forma_pagamento
- restaurante_forma_pagamento
- produto
- permissao
- grupo
- usuario
- grupo_permissao
- usuario_grupo Utilização de propriedades de spring.jpa.properties.javax.persistence.schema-generation.scripts
- action: tipo de ação desejada (create)
- create-target: arquivo de destino
Callback afterMigrate e configuração de locations para o flyway
Formas de reparação de erros em migrações
- Exclusão da linha da migração na tabela flyway-schema e correção da migração
- mvnw flyway:repair -Dflyway.cofigFiles=
Criação das migrações e das entidades Pedido e ItemPedido considerando todos os relacionamentos envolvidos.
Anotação das exceções EntidadeEmUsoException e EntidadeNaoEncontradaException com @ResponseStatus.
Alteração do controller de Cozinha
Utilização da classe ResponseStatusException para lançamento de exceções customizadas com status HTTP e mensagem de erro.
WebServerInputException herda de ResponseStatusException e retorna código 400 (Bad Request) por padrão.
Classe EntidadeNaoEncontradaException herda de ResponseStatusException
Refatoração do código do controlador de cozinha.
Criação do método buscarOuFalhar na classe de serviços de cozinha
Refatoração dos serviços e controladores de restaurante, cidade e estado
Criação da exceção de negócio (NegocioException) e refatoração do controlador de cidade para utilizar a exceção
Refatoração do controlador de restaurante para utilizar a exceção NegocioException
Criação de classe de exceção específica para estado não encontrado (EstadoNaoEncontradoException).
Hierarquia de exceções: Exception -> RuntimeException -> NegocioException -> EntidadeNaoEncontradaException -> EstadoNaoEncontradoException
Criação e configuração de exceções de granularidade fina para Restaurante, Cidade e Cozinha.
Tratamento de exceções no controlador de cidades com mensagens customizadas.
Criação de classe para customização da mensagem de erro (Problema.java).
Uso da anotação @Builder para criar um construtor (builder) para a classe Problema.
Criação de classe de tratamento de exceções de forma global (ApiExceptionHandler) utilizando anotação @ControllerAdvice.
Implementação de tratamento de exceção de tipo de mídia não suportado (HttpMediaTypeNotSupportedException).
Implementação de tratamento de exceção global para EntidadeEmUsoException.
Herança de ResponseEntityExceptionHandler em ApiExceptionHandler.
Código do método tratarHttpMediaTypeNotSupportedException comentado por conflito
Sobrescrita da função handleExceptionInternal para customizar o corpo da respota.
Refatoração dos métodos de lançamento de exceção para utilizar o handleExceptionInternal.
Corpo da resposta com parâmetros definidos pela RFC 7807 (status, type, title, detail).
Criação do enum ProblemType.
Criação do método createProblemBuilder em ApiHandlerException.
Refatoração do método handleEntidadeNaoEncontradaException para utilizar a RFC 7807
Refatoração dos métodos handleEntidadeEmUsoException e handleNegocioException de acordo com a RFC 7807.
Sobrescrita do método handleHttpMessageNotReadable para customização seguindo RFC
Tratamento de tipo de dados inválido.
Adição no pom.xml da dependência Apache Commons Lang3 (commons-lang3)
Alteração de application.properties para lançamento de erros na desserialização
- Propriedade ignorada - spring.jackson.deserialization.fail-on-ignored-properties=true
- Propriedade inexistente - spring.jackson.deserialization.fail-on-unknown-properties=true
Tratamento da exceção PropertyBindingException para customização da mensagem de erro de propriedade ignorada e inexistente
Lançamento da exceção PropertyBindingException em caso de atualização parcial (método HTTP Patch).
Configuração do lançamento da exceção através do objectMapper.
Relançamento da exceção IllegalArgumentException como HttpMessageNotReadableException no método merge do controlador de restaurante.
Implementação da exceção de tipo de parâmetro inválido na URL. Configuração personalizada da exceção MethodArgumentTypeMismatchException
Configuração personalizada da exceção NoHandlerFoundException
Configuração personalizada das mensagens de exceção para situações não previstas em outros métodos
Adição dos atributos userMessagee timestamp na classe Problem.
Refatoração do método createProblemBuilder e dos métodos de lançamento de exceções para tratar os novos atributos criados.
Criação de mensagem genérica de erro para o usuário.
Adição da dependência spring-boot-starter-validation. Essa adição deve ser feita em qualquer projeto Spring a partir da versão 2.3.0
Adição de validação NotNull no atributo nome da classe Restaurante.
Configuração do controlador com anotação @Valid.
Customização da exceção handleMethodArgumentNotValid
Extensão da classe Problem com a lista de campos não validados.
Adição das constraints @NotEmpty e @NotBlank para o atributo nome e @DecimalMin e @PositiveOrZero para o atributo taxaFrete.
Observação: Algumas validações possuem anotações depreciadas do pacote hibernate.validator. Não utilizar estas validações.
Validação para a passagem de cozinha com id nulo na entidade Restaurante.
Observação: Quebra o cadastro de cozinhas
Criação de grupos de validação para cada classe.
Alteração da anotação @Valid para @Validated no controlador para aceitar a configuração dos grupos.
Uso da anotação @ConvertGroup para simplificar a validação em cascata
Validação nos modelos de Cidade e Estado e controladores de Cidade, Estado e Cozinha (métodos atualizar e adicionar)
Alteração da mensagem de validação através do parâmetro message da anotação
Criação do arquivo message.properties para configuração de mensagens de erro globais.
Configuração da exceção para buscar no arquivo message.properties utilizando uma isntância da classe MessageSource
Customização das mensagens de validação das classes Cozinha, Restaurante, Estado e Cidade.
Adição da constraint NotNull em taxaFrete.
Customização das mensagens do Bean Validation através do arquivo ValidationMessages.properties.
Resource Bundle do Spring (message.properties) tem precedência sobre o do Bean Validation (ValidationMessages.properties).
Possível criar propriedade customizada no ValidationMessages e utilizar na propriedade message da anotação
Criação de classe de configuração de validação (ValidationConfig.java).
Mudança da configuração de grupos de validação para o pacote core.validation e refatoração nas classes necessárias
Criação de anotação customizada para validação.
Aula 9.16 - Criando constraints de validação customizadas com implementação de ConstraintValidator
Implementação de anotação com regra de negócio específica
Implementação de validação para a classe com verificação de atributos específicos.
Correção do problema de não mostrar os campos de validação na mensagem de validação (campo fields).
Customização da mensagem de erro de ValorZeroIncluiDescricao.
Validação programática para a atualização parcial de restaurante.
Criação de exceção ValidacaoException.
Configuração do lançamento de ValidacaoException através da classe ApiExceptionHandler
Testes de integração para verificar cadastro de cozinha com sucesso e com problemas de validação relativos ao nome da cozinha.
Testes de integração para exclusão de cozinhas
Adição do plugin Maven Failsafe para agilizar o processo de build.
Renomeação da classe CadastroCozinhaIntegrationTests para CadastroCozinhaIT (padrão do Maven Failsafe).
Implementação de testes de API.
COnfiguração do teste para subir um servidor em uma porta específica.
Log de erros com método enableLoggingOfRequestAndResponseIfValidationFails
Validação do corpo da resposta com .body e utilização dos métodos hasItems e hasSize da biblioteca Hamcrest
Criação de método que executa antes de todos os testes com anotação @Before.
Configuração do log de erros, porta e caminho padrão no método setup
Aula 10.12 - Voltando o estado inicial do banco de dados para cada execução de teste com callback do Flyway
Configuração do Flyway para executar a migração de dados antes de cada teste.
Criação do arquivo application-test.properties com configuração do banco de dados de teste.
Utilização da anotação @TestPropertySource para utilização dos parâmetros de configuração de testes.
Criação da classe utilitária DatabaseCleaner.
Teste de busca por id de cozinha.
Uso do método pathParams para configuração dos parâmetros
Refatoração do códiogo para maior legibilidade e facilidade de manutenção.
Carregamento de arquivo JSON externo com o método getContentFromResource da classe ResourceUtils, criada especificamente para esse fim.
Utilização de contador automático para número de cozinhas a partir do repositório.
Utilização do JsonIgnoreProperties para ignorar propriedade. O parâmetro allowGetters permite que seja ignorado apenas na serialização
Criação da classe mixin de restaurante para configurar as anotações do Jackson.
Classe de configuração de mixin (JacksonMixinModule) no pacote core.jackson
Criação dos Mixin de Cidade e Cozinha.
Alteração no modelo de Cidade para rejeitar requisições PUT e POST com o nome do estado (com @JsonIgnoreProperties)
Alteração do tipo de datas de LocalDateTime para OffsetDateTime para acrescentar o offset de timezone.
Configuração da aplicação para utilizar o timezone UTC (independente do sistema operacional)
Refatoração das classes Usuario, Pedido, Problem e ApiExceptionHandler para utilização de OffsetDateTime
Criação de DTOs para as classes Cozinha e Restaurante.
Alteração do método listar do RestauranteController para retornar RestauranteModel.
Implementação dos métodos toModel e toCollectionModel no RestauranteController.
Refatoração dos métodos de RestauranteController para utilização do DTO.
Criação de DTOs para entradas de restaurantes e id de cozinha.
Alteração no controller de restaurante para utilização do DTO de entrada de dados.
Exclusão do mixin de restaurante.
Criação da classe RestauranteInputDisassembler e transferência do método toDomainObject para esta classe.
Refatoração do controller de restaurante.
Adição da dependência do modelmapper no pom.xml
Criação de classe de configuração do ModelMapper (ModelMapperConfig.java).
Utilização do ModelMapper no assembler e disassembler de restaurantes.
Customização da propriedade taxaFrete na representação através de mapeamento na classe de configuração do ModelMapper.
Criação do método copyToDomainObject na classe RestauranteInputDisassembler para cópia de propriedades (necessário na atualização)
Mensagens de validação sem customização com DTOs. Ajuste do arquivo message.properties para reconhecimento do DTO
Criação de DTOs, assemblers e disassemblers para cidade, estado e cozinha
Criação do atributo ativo no banco de dados e no modelo.
Implementação dos endpoints de ativação (put /restaurantes/{id}/ativo) e inativação (delete /restaurantes/{id}/ativo) de restaurantes
Criação do modelo de representação de endereço (EnderecoModel).
Configuração customizada para apresentação apenas do nome do estado no endereço.
Criação do modelo de representação de entrada de endereço (EnderecoInput).
Refatoração para fazer endereço ser atributo obrigatório na adição e atualização de restaurantes.
Implementação dos endpoints de listagem, busca, adição, atualização e exclusão de grupos