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

[Bug] org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement [Values of types "TINYINT" and "BOOLEAN" are not comparable

Ao testar a interface MedicoRepository, encontrei o erro abaixo, para o método escolherMedicoAleatorioLivreNaDataCenario2, fala que os valores dos tipos não são comparáveis (tinyint e boolean). Isso foi o que ele montou a query que retornou no stacktrace: 2024-05-02T00:03:29.821-03:00 INFO 26816 --- [ main] c.p.s.c.d.medico.MedicoRepositoryTest : Started MedicoRepositoryTest in 5.723 seconds (process running for 7.335) OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended Hibernate: insert into medicos (ativo, crm, email, bairro, cep, cidade, complemento, logradouro, numero, uf, especialidade, nome, telefone, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, default) Hibernate: select m1_0.id, m1_0.ativo, m1_0.crm, m1_0.email, m1_0.bairro, m1_0.cep, m1_0.cidade, m1_0.complemento, m1_0.logradouro, m1_0.numero, m1_0.uf, m1_0.especialidade, m1_0.nome, m1_0.telefone from medicos m1_0 where m1_0.ativo=true and m1_0.especialidade=? and m1_0.id not in (select c1_0.medico_id from consultas c1_0 where c1_0.data=? and c1_0.motivo_cancelamento is null) order by rand() fetch first 1 rows only 2024-05-02T00:03:30.765-03:00 WARN 26816 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 90110, SQLState: 90110 2024-05-02T00:03:30.765-03:00 ERROR 26816 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : Values of types "TINYINT" and "BOOLEAN" are not comparable; SQL statement: select m1_0.id,m1_0.ativo,m1_0.crm,m1_0.email,m1_0.bairro,m1_0.cep,m1_0.cidade,m1_0.complemento,m1_0.logradouro,m1_0.numero,m1_0.uf,m1_0.especialidade,m1_0.nome,m1_0.telefone from medicos m1_0 where m1_0.ativo=true and m1_0.especialidade=? and m1_0.id not in (select c1_0.medico_id from consultas c1_0 where c1_0.data=? and c1_0.motivo_cancelamento is null) order by rand() fetch first 1 rows only [90110-224]

A classe, eu criei dessa forma, seguindo os exemplos que foram explicados no curso, incluindo os métodos privados. Existe algum tipo de tratamento que eu preciso fazer para ajustar para esse tipo de erro?

7 respostas
package com.plano.saude.cadastro.domain.medico;

import com.plano.saude.cadastro.domain.beneficiario.Beneficiario;
import com.plano.saude.cadastro.domain.beneficiario.DadosCadastroBeneficiario;
import com.plano.saude.cadastro.domain.consulta.Consulta;
import com.plano.saude.cadastro.domain.documento.DadosCadastroDocumento;
import com.plano.saude.cadastro.domain.documento.TipoDocumento;
import com.plano.saude.cadastro.domain.endereco.DadosEndereco;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.test.context.ActiveProfiles;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.TemporalAdjusters;
import java.util.Collections;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@ActiveProfiles("test")
class MedicoRepositoryTest {

    @Autowired
    private MedicoRepository medicoRepository;

    @Autowired
    private TestEntityManager em;

    @Test
    @DisplayName("Deveria retornar medico, quando médico cadastrado estiver disponível na data")
    void escolherMedicoAleatorioLivreNaDataCenario2() {
        // given ou arrange
        var proximaSegundaAs10 = LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.MONDAY)).atTime(10, 0);

        var medico = cadastrarMedico(1L, "Medico", "medico@plano.saude", "123456", Especialidade.CARDIOLOGIA);

        // when ou act
        var medicoLivre = medicoRepository.escolherMedicoAleatorioLivreNaData(Especialidade.CARDIOLOGIA, proximaSegundaAs10);

        // then ou assert
        assertThat(medicoLivre).isEqualTo(medico);
    }

    private void cadastrarConsulta(Medico medico, Beneficiario beneficiario, LocalDateTime data) {
        em.persist(new Consulta(1L, medico, beneficiario, data));
    }

    private Medico cadastrarMedico(Long id, String nome, String email, String crm, Especialidade especialidade) {
        var medico = new Medico(dadosMedico(id, nome, email, crm, especialidade));
        em.persist(medico);

        return medico;
    }

    private Beneficiario cadastrarBeneficiario(Long id, String nome, String telefone, LocalDate dataNascimento, LocalDate dataInclusao, LocalDate dataAtualizacao, List<DadosCadastroDocumento> dadosCadastroDocumentos, DadosEndereco dadosEndereco){
        var beneficiario = new Beneficiario(dadosBeneficiario(id, nome, telefone, dataNascimento, dataAtualizacao, dataInclusao, dadosCadastroDocumentos, dadosEndereco));
        em.persist(beneficiario);

        return beneficiario;
    }

    private DadosCadastroBeneficiario dadosBeneficiario(Long id, String nome, String telefone, LocalDate dataNascimento, LocalDate dataAtualizacao, LocalDate dataInclusao, List<DadosCadastroDocumento> dadosCadastroDocumentos, DadosEndereco dadosEndereco) {
        return new DadosCadastroBeneficiario(
                nome, telefone, dataNascimento, dataInclusao, dataAtualizacao, dadosCadastroDocumentos, dadosEndereco
        );
    }

    private DadosCadastroMedico dadosMedico(Long id, String nome, String email, String crm, Especialidade especialidade) {
        return new DadosCadastroMedico(
                nome,
                email,
                crm,
                "1198724827",
                especialidade,
                dadosEndereco()
        );
    }

    private List<DadosCadastroDocumento> dadosCadastroDocumento() {
        return Collections.singletonList(new DadosCadastroDocumento(
                TipoDocumento.CARTEIRA_NACIONAL_HABILITACAO,
                "123456789",
                LocalDate.of(2010, 03, 18),
                "Documento Teste",
                LocalDate.of(2023, 12, 18),
                LocalDate.of(2023, 12, 18)
        ));
    }

    private DadosEndereco dadosEndereco() {
        return new DadosEndereco(
                "rua xpto",
                "bairro",
                "00000000",
                "Brasilia",
                "DF",
                null,
                null
        );
    }
}

Oi!

Nessa query, na interface repository, tem esse trecho:

m.ativo = 1

Altere para:

m.ativo = true

Oi!

Então, esse trecho já está alterado para "true".

@Query("""
            select m from Medico m
            where
            **m.ativo = true**
            and
            m.especialidade = :especialidade
            and
            m.id not in(
                select c.medico.id from Consulta c
                where
                c.data = :data
            and
                c.motivoCancelamento is null
            )
            order by rand()
            limit 1
            """)
    Medico escolherMedicoAleatorioLivreNaData(Especialidade especialidade, LocalDateTime data);

Altera para para m.ativo = 1 então. Você está usando o H2 ao invés do MySQL e nele deve ter esse problema de compatibilidade.

Eu já tinha alterado antes para m.ativo = 1 e também deu erro, faltou eu ter informado, tanto que também não funciona, quando eu executo a aplicação. O erro do teste foi este aqui: Insira aqui a descrição dessa imagem para ajudar na acessibilidadejava.lang.IllegalStateException: Failed to load ApplicationContext for [MergedContextConfiguration@610115e8 testClass = com.plano.saude.cadastro.domain.medico.MedicoRepositoryTest, locations = [], classes = [com.plano.saude.cadastro.CadastroApplication], contextInitializerClasses = [], activeProfiles = ["test"], propertySourceDescriptors = [], propertySourceProperties = ["org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper=true"], contextCustomizers = [[ImportsContextCustomizer@498d7c57 key = [org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration, org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration, org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManagerAutoConfiguration, org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration, org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration, org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration, org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.JdbcClientAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@c430e6c, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@72bc6553, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory$DisableAutoConfigurationContextCustomizer@7bd7d6d6, org.springframework.boot.test.autoconfigure.actuate.observability.ObservabilityContextCustomizerFactory$DisableObservabilityContextCustomizer@1f, org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizer@34be3d80, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@114654d0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizer@6f43c82, org.springframework.boot.test.context.SpringBootTestAnnotation@e7d99fbd], contextLoader = org.springframework.boot.test.context.SpringBootContextLoader, parent = null]

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'medicoRepository' defined in com.plano.saude.cadastro.domain.medico.MedicoRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration: Could not create query for public abstract com.plano.saude.cadastro.domain.medico.Medico com.plano.saude.cadastro.domain.medico.MedicoRepository.escolherMedicoAleatorioLivreNaData(com.plano.saude.cadastro.domain.medico.Especialidade,java.time.LocalDateTime); Reason: Validation failed for query for method public abstract com.plano.saude.cadastro.domain.medico.Medico com.plano.saude.cadastro.domain.medico.MedicoRepository.escolherMedicoAleatorioLivreNaData(com.plano.saude.cadastro.domain.medico.Especialidade,java.time.LocalDateTime)

Caused by: java.lang.IllegalArgumentException: Validation failed for query for method public abstract com.plano.saude.cadastro.domain.medico.Medico com.plano.saude.cadastro.domain.medico.MedicoRepository.escolherMedicoAleatorioLivreNaData(com.plano.saude.cadastro.domain.medico.Especialidade,java.time.LocalDateTime)

Caused by: java.lang.IllegalArgumentException: org.hibernate.query.SemanticException: Cannot compare left expression of type 'java.lang.Boolean' with right expression of type 'java.lang.Integer'

Caused by: org.hibernate.query.SemanticException: Cannot compare left expression of type 'java.lang.Boolean' with right expression of type 'java.lang.Integer'

Pode ver, que nem a aplicação inicia, por conta dessa alteração na dependência: Insira aqui a descrição dessa imagem para ajudar na acessibilidade

solução!

Altere então no H2 para definir a coluna como boolean. Crie uma nova migration:

ALTER TABLE medicos ALTER COLUMN ativo BOOLEAN;

Altere o atributo na entidade Medico para Boolean também e na query deixe como ativo = true.

Assim tudo fica padronizado como boolean.

Era o problema do tipo no banco mesmo, como sugeriu. Alterei e funcionou. Obrigado!