package com.ankurm.hibernatedemo; import com.ankurm.hibernatedemo.immutable.WideImmutableRow; import com.ankurm.hibernatedemo.immutable.WideMutableRow; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; import java.util.List; import org.hibernate.Session; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; /** * Backs docs/07-immutable-entities.md, "Does @Immutable actually cost less at flush time?". * *
This is NOT a rigorous benchmark: it is one Spring context, in a shared container, timed
* with {@code System.nanoTime()} around a single {@code flush()} call per entity type, no JMH,
* no warm-up isolation, no forked JVM. Treat the numbers as indicative of the right order of
* magnitude and direction, not as a citable throughput claim. Rows: 4000 per entity type,
* 12 String columns each, loaded fully into the persistence context, then flushed with NO
* pending changes -- so any time difference is purely Hibernate deciding "does this entity need
* an UPDATE", not the cost of writing one.
*/
@SpringBootTest
class ImmutableFlushCostTest {
private static final Logger DEMO = LoggerFactory.getLogger("DEMO");
private static final int ROW_COUNT = 4000;
@Autowired
private EntityManagerFactory emf;
@Test
void flushingManyLoadedEntities_immutableSkipsDirtyCheck_mutableDoesNot() {
seedMutable(ROW_COUNT);
seedImmutable(ROW_COUNT);
// Warm-up pass (JIT, connection pool, buffer pool) -- discarded.
timeMutableFlush();
timeImmutableFlush();
long mutableNanos = timeMutableFlush();
long immutableNanos = timeImmutableFlush();
DEMO.info("flush() over {} loaded MUTABLE rows (12 cols, no pending changes): {} ms",
ROW_COUNT, mutableNanos / 1_000_000.0);
DEMO.info("flush() over {} loaded @Immutable rows (12 cols, no pending changes): {} ms",
ROW_COUNT, immutableNanos / 1_000_000.0);
DEMO.info("ratio (mutable / immutable) = {}", (double) mutableNanos / immutableNanos);
DEMO.info("CAVEAT: single-run, shared-container timing -- indicative only, not a benchmark result.");
}
private long timeMutableFlush() {
EntityManager em = emf.createEntityManager();
Session session = em.unwrap(Session.class);
em.getTransaction().begin();
List