Files

112 lines
4.3 KiB
Java
Executable File

package com.ankurm.hibernatedemo.naturalid;
import static org.assertj.core.api.Assertions.assertThat;
import com.ankurm.hibernatedemo.HibernateDemoApplication;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* What actually happens if you mutate a {@code @NaturalId} field Hibernate believes is
* immutable (the default), versus one explicitly marked {@code mutable = true}?
*
* <p>Docs: docs/06-natural-ids.md, Topic 3.
*/
@SpringBootTest(classes = HibernateDemoApplication.class)
class NaturalIdMutabilityTest {
@Autowired
private EntityManagerFactory emf;
@Test
void mutatingAnImmutableNaturalId_throwsOnFlush() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
ImmutableNaturalIdEntity e = new ImmutableNaturalIdEntity("CODE-A");
em.persist(e);
em.getTransaction().commit();
Long id = e.getId();
em.close();
EntityManager em2 = emf.createEntityManager();
Session session = em2.unwrap(Session.class);
session.getTransaction().begin();
ImmutableNaturalIdEntity managed = session.find(ImmutableNaturalIdEntity.class, id);
managed.setCode("CODE-B"); // mutate the field Hibernate believes is immutable
HibernateException ex = org.junit.jupiter.api.Assertions.assertThrows(
HibernateException.class,
() -> {
session.flush();
});
System.out.println("RESULT[naturalid-immutable-mutation]: flushing a changed IMMUTABLE natural id threw: "
+ ex.getClass().getName() + ": " + ex.getMessage());
session.getTransaction().rollback();
em2.close();
assertThat(ex.getMessage()).containsIgnoringCase("immutable");
}
@Test
void mutatingAMutableNaturalId_flushesFine_andUpdatesTheColumn() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
MutableNaturalIdEntity e = new MutableNaturalIdEntity("CODE-X");
em.persist(e);
em.getTransaction().commit();
Long id = e.getId();
em.close();
EntityManager em2 = emf.createEntityManager();
em2.getTransaction().begin();
MutableNaturalIdEntity managed = em2.find(MutableNaturalIdEntity.class, id);
managed.setCode("CODE-Y");
em2.getTransaction().commit(); // should NOT throw
em2.close();
EntityManager em3 = emf.createEntityManager();
Object raw = em3.createNativeQuery(
"select code from mutable_natural_id_entity where id = " + id).getSingleResult();
em3.close();
System.out.println("RESULT[naturalid-mutable-mutation]: flush succeeded, DB column now = " + raw);
assertThat(raw).isEqualTo("CODE-Y");
}
@Test
void mutatingAMutableNaturalId_staleLookupByOldValue_noLongerResolves() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
MutableNaturalIdEntity e = new MutableNaturalIdEntity("CODE-OLD");
em.persist(e);
em.getTransaction().commit();
Long id = e.getId();
em.close();
EntityManager em2 = emf.createEntityManager();
em2.getTransaction().begin();
MutableNaturalIdEntity managed = em2.find(MutableNaturalIdEntity.class, id);
managed.setCode("CODE-NEW");
em2.getTransaction().commit();
em2.close();
EntityManager em3 = emf.createEntityManager();
Session session = em3.unwrap(Session.class);
MutableNaturalIdEntity byOld = session.bySimpleNaturalId(MutableNaturalIdEntity.class).load("CODE-OLD");
MutableNaturalIdEntity byNew = session.bySimpleNaturalId(MutableNaturalIdEntity.class).load("CODE-NEW");
em3.close();
System.out.println("RESULT[naturalid-mutable-stale-lookup]: byNaturalId(\"CODE-OLD\") = " + byOld
+ ", byNaturalId(\"CODE-NEW\") = " + (byNew == null ? "null" : byNew.getId()));
assertThat(byOld).isNull();
assertThat(byNew).isNotNull();
assertThat(byNew.getId()).isEqualTo(id);
}
}