Solucionado (ver solução)
Solucionado
(ver solução)
5
respostas

Como Lidar com validação de unique?

Srxs, Bom dia. Estou com uma dúvida de como lidar com a validação de unique.

Suponha a classe model Consultor:

public class Consultor extends Model {

    @NotNull(message = "O nome não pode ser nulo")
    @NotBlank(message = "O nome deve ser preenchido")
    @Column(nullable = true, unique = true, length = 512)
    private String nome;

    @NotNull(message = "O email não pode ser nulo")
    @NotBlank(message = "O email deve ser preenchido")
    @Column(nullable = false, unique = true, length = 512)
    private String email;

    @Column(nullable = true, unique = false, columnDefinition = "TEXT")
    private String telefone;

    ...

Note que, o email e o nome devem ser unique's. Como devo validar isso na minha API rest pra não devolver um stack trace em caso de erro?

Atualmente meu código esta assim:

    @PostMapping
    public ResponseEntity<Consultor> createConsultor(@Valid Consultor consultor, BindingResult bindingResult, UriComponentsBuilder uriBuilder) {

        consultor = consultorService.save(consultor);

        URI uri = uriBuilder.path("/api/consultor/{id}").buildAndExpand(consultor.getId()).toUri();

        return ResponseEntity.created(uri).body(consultor);
    }
@org.springframework.web.bind.annotation.RestControllerAdvice
public class ErroValidacaoHandler {

    @Autowired
    private MessageSource messageSource;

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public List<ErroDeFormulario> handle(MethodArgumentNotValidException exception) {
        List<ErroDeFormulario> erros = new ArrayList<>();
        List<FieldError> fieldErrors = exception.getBindingResult().getFieldErrors();

        for (FieldError err : fieldErrors) {
            erros.add(new ErroDeFormulario(err.getField(), messageSource.getMessage(err, Locale.getDefault())));
        }

        //
        return erros;
    }

}
5 respostas

Oi,

Essa validação no caso seria de regra de negócio, sendo que o Bean Validation não seria utilizado.

No seu método save da classe ConsultorService você deve, antes de salvar no banco com o repository, verificar se já existe um registro com o mesmo email cadastrado no banco e lançar uma exception em caso positivo.

Professor, boa tarde. Muito obrigado pela resposta. Eu fiz a implementação e gostaria, se possível de sua avaliação. Estou com dúvida se isto é o suficiente ou se eu deveria ter um RestControllerAdvice para este caso também.

    public Consultor save(Consultor consultor) {

        Consultor consultorRepetido = get(consultor.getEmail(), consultor.getId());
        if (consultorRepetido != null) {
            throw new IllegalArgumentException("Já existe um consultor com esse email");
        }

        return consultorRepository.save(consultor);

    }
public interface ConsultorRepository extends JpaRepository<Consultor, UUID> {

    @Query("SELECT c FROM Consultor c WHERE c.email = ?1 and c.id != ?2")
    Consultor findUserByEmailAndDifferentId(String email, UUID uuid);

}

O que eu costumo fazer é criar uma exception no projeto(ex: ValidacaoException) e criar um novo método no controllerAdvice para tratar essa exception e devolver BAD_REQUEST nesse caso.

Obs: como você já tem uma classe service no projeto, a recomendação é colocar esse if na service ao invés de deixar no controller.

solução!

Obrigado professor, acho que consegui. Segue o código. Espero que, caso esteja correto, ajude outros alunos.

/**
 *
 * Exceção lançada quando há a tentativa de um cadastro e uma coluna repetida é
 * violada. Ex: A coluna de email é Unica e Existe um consultor c1 com um email
 * unico m e se tenta cadastrar um consultor c2 com o mesmo email m
 *
 */
public class UniqueException extends RuntimeException {

    /**
     * Campo/Field em que houve a violação
     */
    private String campo;

    public UniqueException(String string) {
        super(string);
    }

    public UniqueException(String mensagem, String campo) {
        super(mensagem);
        this.campo = campo;
    }

    public String getCampo() {
        return campo;
    }



}
   public Consultor save(Consultor consultor) {

        Consultor consultorRepetido = get(consultor.getEmail(), consultor.getId());
        if (consultorRepetido != null) {
            throw new UniqueException("Já existe um consultor com esse email", "email");
        }

        return consultorRepository.save(consultor);

    }
@org.springframework.web.bind.annotation.RestControllerAdvice
public class ErroValidacaoHandler {

    @Autowired
    private MessageSource messageSource;

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public List<ErroDeFormulario> handleMethodArgumentNotValidException(MethodArgumentNotValidException exception) {
        List<ErroDeFormulario> erros = new ArrayList<>();
        List<FieldError> fieldErrors = exception.getBindingResult().getFieldErrors();

        for (FieldError err : fieldErrors) {
            erros.add(new ErroDeFormulario(err.getField(), messageSource.getMessage(err, Locale.getDefault())));
        }

        //
        return erros;
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(UniqueException.class)
    public ErroDeFormulario handleUniqueException(UniqueException exception) {

        return new ErroDeFormulario(exception.getCampo(), exception.getMessage());

    }

}

Show! Era desse jeito mesmo :D

Bons estudos!