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

Validação de chave estrangeira

Refazendo os passos do professor nessa aula, fiz um teste de criar uma conta com Id inexistente no banco:

Conta conta = new Conta();
conta.setId(9999L);

e associei essa conta a uma novo Cliente:

Cliente cliente = new Cliente();
cliente.setConta(conta);

E inseri o cliente no banco:

entityManager.getTransaction().begin()
entityManager.persist(cliente);
entityManager.getTransaction().commit();

Eu esperava que a inserção não fosse ocorrer, pois não existe a conta com id 9999 no meu banco. Mas para minha surpresa o cliente foi inserido com o id 9999 na coluna conta_id. A conta continua não existindo. Podem me explicar o porque isso é aceito? Existe alguma maneira de impedir que uma inserção dessa maneira ocorra?

2 respostas

Fala, krekon!

Criei a classe de testes com seu código abaixo:

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class TesteKrekon {
    public static void main(String[] args) {
        Conta conta = new Conta();
        conta.setId(9999L);

        Cliente cliente = new Cliente();
        cliente.setConta(conta);

        EntityManagerFactory emf = Persistence.createEntityManagerFactory("alura");
        EntityManager em = emf.createEntityManager();

        em.getTransaction().begin();
        em.persist(cliente);
        em.getTransaction().commit();
    }
}

Este código retornou uma Exception devido a chave estrangeira.

Caused by: java.sql.SQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (`alura_jpa`.`cliente`, CONSTRAINT `FKajhxkga86ursgptf0lm09qpj0` FOREIGN KEY (`conta_id`) REFERENCES `conta` (`id`))
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:117)
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97)
    at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
    at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:953)
    at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1092)
    at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1040)
    at com.mysql.cj.jdbc.ClientPreparedStatement.executeLargeUpdate(ClientPreparedStatement.java:1347)
    at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdate(ClientPreparedStatement.java:1025)
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:197)
    ... 20 more

Se eu removo o tipo de geração "IDENTITY" da classe conta, já passa a dar uma Exception pq meu objeto ainda é transient.

Caused by: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : br.com.alura.jpa.modelo.Cliente.conta -> br.com.alura.jpa.modelo.Conta
    at org.hibernate.engine.spi.CascadingActions$8.noCascade(CascadingActions.java:379)
    at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:167)
    at org.hibernate.event.internal.AbstractFlushingEventListener.cascadeOnFlush(AbstractFlushingEventListener.java:158)
    at org.hibernate.event.internal.AbstractFlushingEventListener.prepareEntityFlushes(AbstractFlushingEventListener.java:148)
    at org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:81)
    at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39)
    at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:102)
    at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1352)
    ... 9 more
solução!

Comparando o código com o Alexandre Serrano e na tentativa e erro descobrimos que o problema era na configuração do dialeto do meu hibernate. Eu estava utilizando a configuração:

<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>

Com esse dialeto no momento da criação das tabelas o hibernate não criava as chaves estrangeiras no banco. Alterando para o dialeto:

<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>

As chaves estrangeiras passaram a ser criadas e a validação da chave passou a ser feita.