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

[Dúvida] Erro de incompatibilidade de tipos

Quando rodo a seguinte requisição de agendamento;

Insira aqui a descrição dessa imagem para ajudar na acessibilidade

Estou rerebendo o erro: Argument [2] of type [java.lang.Long] did not match parameter type [med.voll.api.domain.medico.MedicoEntity (n/a)]

Entendo que está ocorrendo um erro de compatibilidade nos tipos, mas não consigo encontrar algo no código que justifique, vou encaminhar minha classes abaixo.

Insira aqui a descrição dessa imagem para ajudar na acessibilidade

Validador médico Ativo:

Insira aqui a descrição dessa imagem para ajudar na acessibilidade

MedicoRepository:

Insira aqui a descrição dessa imagem para ajudar na acessibilidade

19 respostas

Oi!

Manda sua interface MedicoRepository completa, pois o erro deve ser de outro método nela.

Insira aqui a descrição dessa imagem para ajudar na acessibilidadeAqui está

Acho que o problema é na query do método medicoAleatorio, na parte do subselect:

SELECT c.idMedico FROM ConsultaEntity c

Acho que deveria ser:

SELECT c.medico.id FROM ConsultaEntity c

Tem que ficar de acordo com os nomes dos atributos da entidade e não da tabela no banco de dados.

Mas o atributo idMedico está nomeado como a minha entidade consulta

Insira aqui a descrição dessa imagem para ajudar na acessibilidade

O Select dessa forma não compila (SELECT c.medico.id FROM ConsultaEntity c)

Ah então está certo. Bom, manda o log completo do erro então, pois deve ser outra coisa.

Só está aparecendo esse WARM :

2023-07-19T08:18:59.346-03:00 WARN 13924 --- [nio-8080-exec-3] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.dao.InvalidDataAccessApiUsageException: Argument [2] of type [java.lang.Long] did not match parameter type [med.voll.api.domain.medico.MedicoEntity (n/a)]]

E essa é a query que o Hibernate está rodando para médicos:

Hibernate: select count(*) from medicos m1_0 where m1_0.id=?

Bom dia, algum retorno?

Esse log acabou saindo incompleto.

Coloca um try catch no método agendar da classe service:

public DadosDetalhamentoConsulta agendar(DadosAgendamentoConsulta dados) {
    try {
        if (!pacienteRepository.existsById(dados.idPaciente())) {
            throw new ValidacaoException("Id do paciente informado não existe!");
        }

        if (dados.idMedico() != null && !medicoRepository.existsById(dados.idMedico())) {
            throw new ValidacaoException("Id do médico informado não existe!");
        }

        validadores.forEach(v -> v.validar(dados));

        var paciente = pacienteRepository.getReferenceById(dados.idPaciente());
        var medico = escolherMedico(dados);
        if (medico == null) {
            throw new ValidacaoException("Não existe médico disponível nessa data!");
        }

        var consulta = new Consulta(null, medico, paciente, dados.data(), null);
        consultaRepository.save(consulta);

        return new DadosDetalhamentoConsulta(consulta);
    } catch(Exception e) {
        e.printStackTrace();
        throw new RuntimeException("Erro ao agendar consulta" , e);
    }
}

Meu método agendar esá como void:


public void agendar(DadosAgendamentoConsulta dados) {

        try {
            //Regras de negócio:
            if (!pacientesRepository.existsById(dados.idPaciente())) {
                throw new RuntimeException("O id do paciente informado não existe");
            }

            if (dados.idMedico() != null && !medicoRepository.existsById(dados.idMedico())) {
                throw new RuntimeException("O id do médico informado não existe");
            }

            //Chamando a lista de validações
            validadores.forEach(v -> v.validar(dados));

            //Buscando os dados do objeto medico e paciente no banco
            var medico = escolherMedico(dados);
            if (medico == null) {
                throw new RuntimeException("Não existe médico disponível nessa data!");
            }
            var paciente = pacientesRepository.getReferenceById(dados.idPaciente());

            //atribuindo os dados ao objeto consulta
            var consulta = new ConsultaEntity(null, medico, paciente, dados.data());

            //Salvando o objeto consulta no banco
            consultaRepository.save(consulta);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("Erro ao agendar consulta" , e);
        }
    }

Aqui está parte da stackTrace:

Caused by: java.lang.IllegalArgumentException: Argument [2] of type [java.lang.Long] did not match parameter type [med.voll.api.domain.medico.MedicoEntity (n/a)] at org.hibernate.query.spi.QueryParameterBindingValidator.validate(QueryParameterBindingValidator.java:84) at org.hibernate.query.spi.QueryParameterBindingValidator.validate(QueryParameterBindingValidator.java:31) at org.hibernate.query.internal.QueryParameterBindingImpl.validate(QueryParameterBindingImpl.java:339) at org.hibernate.query.internal.QueryParameterBindingImpl.setBindValue(QueryParameterBindingImpl.java:129) at org.hibernate.query.spi.AbstractCommonQueryContract.setParameter(AbstractCommonQueryContract.java:1066) at org.hibernate.query.spi.AbstractSelectionQuery.setParameter(AbstractSelectionQuery.java:774) at org.hibernate.query.sqm.internal.QuerySqmImpl.setParameter(QuerySqmImpl.java:1329) at org.hibernate.query.sqm.internal.QuerySqmImpl.setParameter(QuerySqmImpl.java:131) at org.springframework.data.jpa.repository.query.QueryParameterSetter$BindableQuery.setParameter(QueryParameterSetter.java:318) at org.springframework.data.jpa.repository.query.QueryParameterSetter$NamedOrIndexedQueryParameterSetter.lambda$setParameter$3(QueryParameterSetter.java:115) at org.springframework.data.jpa.repository.query.QueryParameterSetter$ErrorHandling$1.execute(QueryParameterSetter.java:140) at org.springframework.data.jpa.repository.query.QueryParameterSetter$NamedOrIndexedQueryParameterSetter.setParameter(QueryParameterSetter.java:115) at org.springframework.data.jpa.repository.query.ParameterBinder.bind(ParameterBinder.java:83) at org.springframework.data.jpa.repository.query.ParameterBinder.bind(ParameterBinder.java:75) at org.springframework.data.jpa.repository.query.ParameterBinder.bindAndPrepare(ParameterBinder.java:97) at org.springframework.data.jpa.repository.query.PartTreeJpaQuery$QueryPreparer.invokeBinding(PartTreeJpaQuery.java:312) at org.springframework.data.jpa.repository.query.PartTreeJpaQuery$QueryPreparer.createQuery(PartTreeJpaQuery.java:231) at org.springframework.data.jpa.repository.query.PartTreeJpaQuery.doCreateQuery(PartTreeJpaQuery.java:102) at org.springframework.data.jpa.repository.query.AbstractJpaQuery.createQuery(AbstractJpaQuery.java:234) at org.springframework.data.jpa.repository.query.JpaQueryExecution$ExistsExecution.doExecute(JpaQueryExecution.java:285) at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:90) at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:148) at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:136) at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:136) at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:120) at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:164) at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:143) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:72) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:391) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137) ... 121 more 2023-07-20T11:03:13.547-03:00 WARN 8080 --- [nio-8080-exec-2] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [java.lang.RuntimeException: Erro ao agendar consulta]

Ele não exclarece muito mais, mas aparentemente ele está havendo uma incompatibilidade no segundo parâmetro da query medicoAleatorio né isso?

Na stacktrace não apreceu em quqla consulta foi, mas acho que não é no medicoRepository. Manda aqui o código dos seus outros repository

@Repository
public interface MedicoRepository extends JpaRepository<MedicoEntity, Long> {
    Page<MedicoEntity> findAllByAtivoTrue(Pageable paginacao);
    @Query("""
            SELECT m FROM MedicoEntity m
            WHERE
            m.ativo = true
            AND
            m.id NOT IN
               (SELECT c.idMedico.id FROM ConsultaEntity c
               WHERE
               c.data = :data)
            AND
            m.especialidade = :especialidade
            ORDER BY rand()
            LIMIT 1
            """)
    MedicoEntity medicoAleatorio(Especialidade especialidade, LocalDateTime data);
    @Query("""
            SELECT m.ativo 
            FROM MedicoEntity m
            WHERE 
            m.id = :id
            """)
    Boolean findAtivoById(Long id);
}
@Repository
public interface PacientesRepository extends JpaRepository <PacientesEntity, Long> {
    @Query("""
            SELECT p.ativo 
            FROM PacientesEntity p
            WHERE
            p.id = :id
            """)
    Boolean findAtivoById(Long id);
}
@Repository
public interface ConsultaRepository extends JpaRepository<ConsultaEntity, Long> {
    Boolean existsByIdMedicoAndData(Long idMedico, LocalDateTime data);
    Boolean existsByIdPacienteAndDataBetween(Long idPaciente, LocalDateTime primeiroHorario, LocalDateTime ultimoHorario);

}

Aqui estão as minhas entidades:

@Entity
@Table(name = "medicos")
@Getter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = "id")
public class MedicoEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String nome;
    private String email;
    private String telefone;
    private String crm;
    @Enumerated(EnumType.STRING)
    private Especialidade especialidade;
    @Embedded
    private Endereco endereco;
    private Boolean ativo;
    public MedicoEntity(DadosCadastrosMedicos dadosMedicos) {
        this.ativo = true;
        this.nome = dadosMedicos.nome();
        this.email = dadosMedicos.email();
        this.telefone = dadosMedicos.telefone();
        this.crm = dadosMedicos.crm();
        this.especialidade = dadosMedicos.especialidade();
        this.endereco = new Endereco(dadosMedicos.endereco());
    }
    public void atualizarInformacoes(DadosAtualizacaoMedicos dadosMedicos) {
        if (dadosMedicos.nome() != null) {
            this.nome = dadosMedicos.nome();
        }
        if (dadosMedicos.telefone() != null) {
            this.telefone = dadosMedicos.telefone();
        }
        if (dadosMedicos.endereco() != null) {
            this.endereco.atualizarInformacoes(dadosMedicos.endereco());
        }
    }
    public void excluir() {
        this.ativo = false;
    }
}
@Entity
@Table(name = "consultas")
@Getter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = "id")
public class ConsultaEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long idConsulta;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "medico_id")
    private MedicoEntity idMedico;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "paciente_id")
    private PacientesEntity idPaciente;
    private LocalDateTime data;
}
@Entity
@Table(name = "pacientes")
@Getter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = "id")
public class PacientesEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String nome;
    private String email;
    private String telefone;
    private String cpf;
    @Embedded
    private Endereco endereco;
    private boolean ativo;

    public PacientesEntity(DadosCadastrosPacientes dadosPacientes) {
          this.nome = dadosPacientes.nome();
          this.email = dadosPacientes.email();
          this.telefone = dadosPacientes.telefone();
          this.cpf = dadosPacientes.cpf();
          this.endereco = new Endereco(dadosPacientes.endereco());
    }
    public void atualizarInformacoes(DadosAtualizacaoPacientes dadosPacientes) {
        if (dadosPacientes.nome() != null) {
            this.nome = dadosPacientes.nome();
        }
        if (dadosPacientes.telefone() != null) {
            this.telefone = dadosPacientes.telefone();
        }
        if (dadosPacientes.endereco() != null) {
            this.endereco.atualizarInformacoes(dadosPacientes.endereco());
        }
    }
    
    public void excluir() {this.ativo = false;}
}

Professor, provavelmente o erro está em alguma validação da lista de validações, se eu paro de chamar a lista validadores no Service do agendamento a consulta funciona :

Insira aqui a descrição dessa imagem para ajudar na acessibilidade

vou encaminhar as classes do meus validadores para que o senhor possa me ajudar a tentar encontrar o erro, mas a classe ValidadorMedComConsultaMesmoHorario aparecia na stackTrace acredito que o erro possa estar nela.

@Component
public class ValidadorMedComConsultaMesmoHorario implements Validador{
    @Autowired
    private ConsultaRepository consultaRepository;
    public void validar (DadosAgendamentoConsulta dados) {
        var medico = consultaRepository.existsByIdMedicoAndData(dados.idMedico(), dados.data());

        if(medico) {
            throw new RuntimeException("Médico já possui consulta agendada nesse dia e horário!");
        }
    }
}
@Component
public class ValidadorHorarioAntecedencia implements Validador{
    public void validar(DadosAgendamentoConsulta dados) {
        var dataConsulta = dados.data();
        var agora = LocalDateTime.now();
        var diferencaEmMinutos = Duration.between(agora, dataConsulta). toMinutes();

        if(diferencaEmMinutos < 30) {
            throw new RuntimeException("Consulta deve ser agendada com antecedência mínima de 30 minutos!");
        }
    }
}
@Component
public class ValidadorHorarioFuncionamento implements Validador{
    public void validar(DadosAgendamentoConsulta dados) {

        var dataConsulta = dados.data();

        //Checar se é domingo
        var domingo = dataConsulta.getDayOfWeek().equals(DayOfWeek.SUNDAY);
        //Fora do horário de abertura
        var antesDaAbertura = dataConsulta.getHour() < 7;
        //Fora do horário de fechamento (levando em consideração que a ultima consulta começa
        //as 18 hrs e não pode ser marcada consulta após esse horário:
        var depoisDoFechamento = dataConsulta.getHour() > 18;

        if(domingo || antesDaAbertura || depoisDoFechamento) {
            throw new RuntimeException("Consulta fora do horário de funcionamento da clínica");
        }
    }
}
@Component
public class ValidadorMedicoAtivo implements Validador{
    @Autowired
    private MedicoRepository medicoRepository;
    public void validar (DadosAgendamentoConsulta dados) {

        if(dados.idMedico() == null) {
            return;
        }

        var medicoAtivo = medicoRepository.findAtivoById(dados.idMedico());
        if(!medicoAtivo) {
            throw new RuntimeException("Consulta não pode ser agendada para médicos inativos");
        }
    }
}
@Component
public class ValidadorPacienteAtivo implements Validador{
    @Autowired
    private PacientesRepository pacientesRepository;
    public void validar(DadosAgendamentoConsulta dados) {
        if(dados.idPaciente() == null) {
            return;
        }

        var pacienteAtivo = pacientesRepository.findAtivoById(dados.idPaciente());
        if(!pacienteAtivo) {
            throw new RuntimeException("Consulta não pode ser agendada para pacientes inativos!");
        }
    }
}
@Component
public class ValidadorPacienteSemConsultaMesmoDia implements Validador{
      @Autowired
      private ConsultaRepository consultaRepository;
      public void validar (DadosAgendamentoConsulta dados) {
           var primeiroHorario = dados.data().withHour(7);
           var ultimoHorario = dados.data().withHour(18);

           var paciente = consultaRepository.existsByIdPacienteAndDataBetween(dados.idPaciente(), primeiroHorario, ultimoHorario);

           if(paciente) {
               throw new RuntimeException("Paciente já possui consulta agendada nessa data!");
           }

      }
}
public interface Validador {
    void validar(DadosAgendamentoConsulta dados);
}
solução!

O problema está no seu consultaRepository, nesses dois métodos:

Boolean existsByIdMedicoAndData(Long idMedico, LocalDateTime data);
Boolean existsByIdPacienteAndDataBetween(Long idPaciente, LocalDateTime primeiroHorario, LocalDateTime ultimoHorario);

existsByIdMedico Mas na sua classe Consulta o atributo idMedico é do tipo MedicoEntity e não Long. Então os métodos deveriam se chamar:

Boolean existsByIdMedicoIdAndData(Long idMedico, LocalDateTime data);
Boolean existsByIdPacienteIdAndDataBetween(Long idPaciente, LocalDateTime primeiroHorario, LocalDateTime ultimoHorario);