Add Hibernate 7 batches 2-6, batch 7, and batch 8: mapping styles, JPA annotations, natural IDs, @Immutable, stored procedures, in-memory test databases, JNDI mocking, proxies, associations, temporal mapping, named queries, HQL, Criteria API, EntityManager bootstrapping, Ehcache 3 L2 cache configuration, HikariCP connection pooling, Hibernate Validator CDI integration, aggregate functions, sorting, pagination, interceptors, and Hibernate Search 8 (Hibernate 7.4.5.Final + Spring Boot 4.1.1 + JDK 25)
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
package com.ankurm.hibernatedemo.testdb;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.MethodOrderer;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Backs docs/09-testing-in-memory-databases.md, chapter "Schema isolation: create-drop vs rollback vs
|
||||
* DB_CLOSE_DELAY". Uses one shared {@link EntityManagerFactory} across two ordered tests to
|
||||
* PROVE the pollution, then a second, correctly-isolated pair to prove the fix -- both against
|
||||
* plain H2 (the pollution mechanism is not H2-specific, but H2 is what the rest of this repo
|
||||
* already uses for its default datasource).
|
||||
*/
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
class SchemaIsolationTest {
|
||||
|
||||
private static final Logger DEMO = LoggerFactory.getLogger("DEMO");
|
||||
private static SessionFactory pollutedSf;
|
||||
|
||||
@BeforeAll
|
||||
static void openSharedFactory() {
|
||||
// DB_CLOSE_DELAY=-1 is what keeps this in-memory H2 database alive across the many
|
||||
// short-lived connections each Session opens and closes -- without it, H2 tears the
|
||||
// whole in-memory database down the moment the LAST open connection closes, and the
|
||||
// next openSession() call would silently reconnect to a brand-new, empty database
|
||||
// instead of failing loudly.
|
||||
pollutedSf = TestDbSupport.buildSessionFactory(
|
||||
TestDbSupport.Db.H2, "isolation-demo", "create-drop", TestDbWidget.class);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void closeSharedFactory() {
|
||||
pollutedSf.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
void testA_insertsARowWithNoCleanup() {
|
||||
Session session = pollutedSf.openSession();
|
||||
session.beginTransaction();
|
||||
session.persist(new TestDbWidget("AAAAA", 1, true, "left behind by testA"));
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
DEMO.info("testA committed a row and did nothing to clean it up");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
void testB_seesTestAsRow_becauseThereWasNoIsolationBetweenThem() {
|
||||
Session session = pollutedSf.openSession();
|
||||
long count = session.createQuery("select count(w) from TestDbWidget w", Long.class).getSingleResult();
|
||||
session.close();
|
||||
DEMO.info("testB sees {} row(s) -- at least one is testA's leftover, proving pollution when isolation is skipped", count);
|
||||
// The point: testB did not insert anything itself yet, but the row count is already
|
||||
// >= 1, because testA's commit was never rolled back or dropped.
|
||||
assertThat(count).isGreaterThanOrEqualTo(1);
|
||||
}
|
||||
|
||||
// ---- Now the same shape, but done correctly with @Transactional-style manual rollback ----
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
void testC_insertsARow_thenRollsBackInsteadOfCommitting() {
|
||||
Session session = pollutedSf.openSession();
|
||||
session.beginTransaction();
|
||||
session.persist(new TestDbWidget("BBBBB", 2, true, "rolled back by testC, must not survive"));
|
||||
long duringTxn = session.createQuery("select count(w) from TestDbWidget w", Long.class).getSingleResult();
|
||||
session.getTransaction().rollback();
|
||||
session.close();
|
||||
DEMO.info("testC saw {} row(s) DURING its own transaction (includes its own uncommitted insert), then rolled back", duringTxn);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(4)
|
||||
void testD_doesNotSeeTestCsRolledBackRow() {
|
||||
Session session = pollutedSf.openSession();
|
||||
long count = session.createQuery(
|
||||
"select count(w) from TestDbWidget w where w.sku = :sku", Long.class)
|
||||
.setParameter("sku", "BBBBB")
|
||||
.getSingleResult();
|
||||
session.close();
|
||||
DEMO.info("testD looks specifically for testC's SKU 'BBBBB' -- found {} (must be 0: rollback isolated it)", count);
|
||||
assertThat(count).isZero();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user