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

[Bug] org.hibernate.exception.ConstraintViolationException: could not execute statement

Ao tentar fazer o teste da interface MedicoRepository, encontrei o erro abaixo, para o método escolherMedicoAleatorioLivreNaDataCenario1, como se eu não tivesse passando o medico_id para a tabela consulta. Eu até tentei "forçar", adicionando o id nos métodos cadastrarMedico e cadastrarBeneficiario, o que acredito que não seja necessário. Como que eu faço para passar o valor para esse parâmetro? Insira aqui a descrição dessa imagem para ajudar na acessibilidade

8 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 null, quando único médico cadastrado não estiver disponível na data")
    void escolherMedicoAleatorioLivreNaDataCenario1() {
        // 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);
        var beneficiario = cadastrarBeneficiario(
                1L,
                "Beneficiário",
                "234-5678",
                LocalDate.parse("1983-06-18"),
                LocalDate.parse("2023-03-27"),
                LocalDate.parse("2023-03-27"),
                dadosCadastroDocumento(),
                dadosEndereco());
        cadastrarConsulta(medico, beneficiario, proximaSegundaAs10);

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

        // then ou assert
        assertThat(medicoLivre).isNull();
    }

    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!

Manda o código da sua classe Consulta

Oi!

Segue o código da classe:

package com.plano.saude.cadastro.domain.consulta;

import com.plano.saude.cadastro.domain.beneficiario.Beneficiario;
import com.plano.saude.cadastro.domain.medico.Medico;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

@Table(name = "consultas")
@Entity(name = "Consulta")
@Getter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = "id")
public class Consulta {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "medico_id")
    private Medico medico;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "beneficiario_id")
    private Beneficiario beneficiario;

    private LocalDateTime data;

    @Column(name = "motivo_cancelamento")
    @Enumerated(EnumType.STRING)
    private MotivoCancelamento motivoCancelamento;

    public Consulta(Long id, Medico medico, Beneficiario beneficiario, LocalDateTime data) {
    }

    public void cancelar(MotivoCancelamento motivo) {
        this.motivoCancelamento = motivo;
    }
}

Seu construtor está vazio:

public Consulta(Long id, Medico medico, Beneficiario beneficiario, LocalDateTime data) {
}

Precisa atribuir os valores para não ir tudo null pro banco de dados.

Bom, eu atribuí os valores no construtor, como pode ver abaixo, mas mesmo assim ele deu erro. Na classe DadosConsulta que eu criei, eu adicionei como valor as classes DadosCadastroMedico e DadosCadastroBeneficiario, que já existiam para cadastrar médico e beneficiário em outro cenário.

Para eu atribuir os valores que eu preciso e evitar que esse tipo de erro aconteça, eu deveria criar uma nova classe record ou não tem problema eu aproveitar a classe que já existe no projeto?

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

jakarta.persistence.EntityExistsException: detached entity passed to persist: com.plano.saude.cadastro.domain.consulta.Consulta

at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:126)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:167)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:173)
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:760)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:738)
at org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager.persist(TestEntityManager.java:92)
at com.plano.saude.cadastro.domain.medico.MedicoRepositoryTest.cadastrarConsulta(MedicoRepositoryTest.java:78)
at com.plano.saude.cadastro.domain.medico.MedicoRepositoryTest.escolherMedicoAleatorioLivreNaDataCenario1(MedicoRepositoryTest.java:53)
at java.base/java.lang.reflect.Method.invoke(Method.java:578)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)

Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: com.plano.saude.cadastro.domain.consulta.Consulta at org.hibernate.event.internal.DefaultPersistEventListener.persist(DefaultPersistEventListener.java:88) at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:77) at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:54) at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:127) at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:754) ... 7 more

solução!

No seu teste você está passando o id como 1L ao instanciar o médico e a consulta. Passe null, pois o id é gerado automático pelo banco de dados.

Funcionou. Obrigado!

Show!!! :)