Você poderia me dar uma ideia de como fazer isso: a criação de uma regra, que ao salvar uma cidade, o sistema não aceita duas cidades com o mesmo nome para o mesmo estado?
Estou usando o Spring Boot
My Cidade entity :
@Getter
@Setter
@Entity
@SQLDelete(sql = "UPDATE Cidade SET ativo = 0 WHERE id = ?")
@Where(clause = "ativo = 1")
public class Cidade extends BaseEntity {
private String nome;
private String sigla;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(nullable = false)
private Estado estado;
private Double populacao;
}
My Cidade Resource:
@RestController
@RequestMapping("/api/cidade")
public class CidadeResource {
@Autowired
private CidadeRepository repository;
@Autowired
private EstadoRepository repositoryEstado;
@Autowired
private CidadeResourceMapper mapper;
@GetMapping(produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<PageDto<CidadeDTO>> getPageWithQuery(@QuerydslPredicate(root = Cidade.class ) Predicate predicate, Pageable pageble) {
return ResponseEntity.ok(convertToPageDto(predicate, pageble));
}
@PostMapping
public ResponseEntity<CidadeDTO> add(@Valid @RequestBody CidadeWriteDTO dto) throws Exception {
Cidade entity = this.mapper.fromDto(dto);
entity = repository.save(entity);
ResponseEntity<CidadeDTO> re = ResponseEntity.ok(this.convertToDto(entity));
return re;
}
@GetMapping(value = "/{id}")
public ResponseEntity<CidadeDTO> get(@PathVariable Integer id) {
return ResponseEntity.ok(convertToDto(repository.findById(id).get()));
}
@PutMapping(value = "/{id}")
public ResponseEntity<CidadeDTO> update(@Valid @RequestBody CidadeWriteDTO dto, @PathVariable Integer id) throws AppException {
Optional<Cidade> entity = repository.findById(id);
if (entity.isPresent()) {
Double qtdPopulacao = 0D;
if (entity.get().getEstado().getPopulacao() == null)
qtdPopulacao = entity.get().getPopulacao();
else {
// Remove a população da cidade (antes da alteração)
qtdPopulacao = entity.get().getEstado().getPopulacao() - entity.get().getPopulacao();
}
this.mapper.merge(entity.get(), dto);
// Adiciona a população da cidade
qtdPopulacao += dto.getPopulacao();
// Atualiza os dados no estado
entity.get().getEstado().setPopulacao(qtdPopulacao);
// Salva a entidade no BD
repositoryEstado.save(entity.get().getEstado());
if (dto.getPopulacao() < 0)
throw new AppException("Valor inválido da quantidade da população");
Cidade mergedEntity = repository.save(entity.get());
ResponseEntity<CidadeDTO> re = ResponseEntity.ok(this.convertToDto(mergedEntity));
return re;
} else {
throw new RuntimeException();
}
}
@DeleteMapping(value = "/{id}")
public void delete(@PathVariable Integer id) throws Exception {
repository.deleteById(id);
}
private PageDto<CidadeDTO> convertToPageDto(Predicate predicate, Pageable pageble) {
Page<Cidade> pageEntity = repository.findAll(predicate, pageble);
return new PageDto<>(StreamSupport.stream(pageEntity.spliterator(), false).map(this::convertToDto)
.collect(Collectors.toList()), pageEntity.getTotalElements());
}
private CidadeDTO convertToDto(Cidade entity) {
return mapper.toDto(entity);
}
}
Onde devo criar a regra para evitar a duplicação da cidade no banco de dados? Eu só quero que haja apenas uma cidade para cada estado quando o usuário pesquisar … …
@pmlm, criei um script .sql para a tal alteração via flyway. Daí ele me retorna um erro:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'flywayInitializer' defined in class path resource [org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration$FlywayConfiguration.class]: Invocation of init method failed; nested exception is org.flywaydb.core.api.FlywayException: Validate failed: Migration checksum mismatch for migration version 20201123123400000
Você não pode alterar o script de uma migration que já foi executada no banco de dados!
Neste caso como você alterou ele acusa que houve mudança no script (checksum), neste caso você teria que remover o script da tabela de controle do flyway (schema_version) e reexecutar, lembrando que, todas os comandos que existem na migration devem ser revertidos da base antes de reexecutar!
A forma mais simples e recomendada é criar uma nova migration só com a criação da restrição UK.
Sim, a restrição UK está funcionando corretamente, para melhorar a mensagem de retorno você poderia tratar o erro através da classe DataIntegrityViolationException.class para retornar um status HTTP 409 - CONFLICT por exemplo, tratando uma mensagem informando que a cidade/estado já existem na base de dados.
@Jonathan_Medeiros, muito obrigado. Eu fiz o drop e deu tudo certo, mas pela sua explicação eu não precisaria fazer o drop do banco inteiro mas apenas do schema_version. Foi isso que entendi. Claro, sei que num ambiente pronto completamente não daria para fazer isso se não perderia os dados, estou construindo ainda e testando.
@Jonathan_Medeiros que legal cara, vou fazer. No caso da classe: DataIntegrityViolationException.class , eu devo criá-la no meu package exceptions não é isso?