From fd4ac92f66bf7c3dfc0c691e8c312c51ff153b73 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Fri, 20 Jun 2025 21:43:04 +0200 Subject: [PATCH] Add options to truncate data and evict cache in tests via SessionFactory extension --- ...oncreteProxyToOneSecondLevelCacheTest.java | 15 +----- .../testing/orm/junit/ClearMode.java | 12 +++++ .../testing/orm/junit/SessionFactory.java | 7 +++ .../orm/junit/SessionFactoryExtension.java | 52 +++++++++++++++++-- 4 files changed, 70 insertions(+), 16 deletions(-) create mode 100644 hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/ClearMode.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/concrete/ConcreteProxyToOneSecondLevelCacheTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/concrete/ConcreteProxyToOneSecondLevelCacheTest.java index 57e1ae371928..6a12ba072aca 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/concrete/ConcreteProxyToOneSecondLevelCacheTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/concrete/ConcreteProxyToOneSecondLevelCacheTest.java @@ -22,15 +22,14 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.stat.Statistics; import org.hibernate.testing.bytecode.enhancement.extension.BytecodeEnhanced; +import org.hibernate.testing.orm.junit.ClearMode; import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.Jira; import org.hibernate.testing.orm.junit.ServiceRegistry; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.hibernate.testing.orm.junit.Setting; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -43,7 +42,7 @@ @Setting(name = AvailableSettings.GENERATE_STATISTICS, value = "true"), @Setting(name = AvailableSettings.USE_SECOND_LEVEL_CACHE, value = "true") }) -@SessionFactory +@SessionFactory(dropData = ClearMode.AFTER_ALL, clearCache = ClearMode.BEFORE_EACH) @BytecodeEnhanced(runNotEnhancedAsWell = true) @Jira("https://hibernate.atlassian.net/browse/HHH-18872") public class ConcreteProxyToOneSecondLevelCacheTest { @@ -144,11 +143,6 @@ private static void assertCacheStats(final Statistics stats, final long hits, fi assertThat( stats.getSecondLevelCachePutCount() ).isEqualTo( puts ); } - @BeforeEach - public void clearCache(SessionFactoryScope scope) { - scope.getSessionFactory().getCache().evictAllRegions(); - } - @BeforeAll public void setUp(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -160,11 +154,6 @@ public void setUp(SessionFactoryScope scope) { } ); } - @AfterAll - public void tearDown(SessionFactoryScope scope) { - scope.getSessionFactory().getSchemaManager().truncateMappedObjects(); - } - @Entity(name = "TestNode") @Cacheable @ConcreteProxy diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/ClearMode.java b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/ClearMode.java new file mode 100644 index 000000000000..1b0e92c75110 --- /dev/null +++ b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/ClearMode.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.testing.orm.junit; + +import org.hibernate.Incubating; + +@Incubating +public enum ClearMode { + BEFORE_EACH, AFTER_EACH, AFTER_ALL, NEVER +} diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/SessionFactory.java b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/SessionFactory.java index 1be61c90b95c..f2f1e7805c30 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/SessionFactory.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/SessionFactory.java @@ -10,6 +10,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.hibernate.Incubating; import org.hibernate.Interceptor; import org.hibernate.resource.jdbc.spi.StatementInspector; @@ -56,4 +57,10 @@ boolean useCollectingStatementInspector() default false; boolean applyCollectionsInDefaultFetchGroup() default true; + + @Incubating + ClearMode dropData() default ClearMode.NEVER; + @Incubating + ClearMode clearCache() default ClearMode.NEVER; + } diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/SessionFactoryExtension.java b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/SessionFactoryExtension.java index a1f6d65ddbf1..9dad02cf8f5c 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/SessionFactoryExtension.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/SessionFactoryExtension.java @@ -28,6 +28,8 @@ import org.hibernate.testing.jdbc.SQLStatementInspector; import org.hibernate.testing.orm.transaction.TransactionUtil; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.TestExecutionExceptionHandler; @@ -46,7 +48,8 @@ * @author Steve Ebersole */ public class SessionFactoryExtension - implements TestInstancePostProcessor, BeforeEachCallback, TestExecutionExceptionHandler { + implements TestInstancePostProcessor, BeforeEachCallback, TestExecutionExceptionHandler, + AfterEachCallback, AfterAllCallback { private static final Logger log = Logger.getLogger( SessionFactoryExtension.class ); private static final String SESSION_FACTORY_KEY = SessionFactoryScope.class.getName(); @@ -92,6 +95,7 @@ public void beforeEach(ExtensionContext context) { if ( sfAnnRef.isEmpty() ) { // assume the annotations are defined on the class-level... // will be validated by the parameter-resolver or SFS-extension + cleanup( context, ClearMode.BEFORE_EACH ); return; } @@ -99,6 +103,8 @@ public void beforeEach(ExtensionContext context) { final SessionFactoryScope created = createSessionFactoryScope( context.getRequiredTestInstance(), sfAnnRef, domainModelScope, context ); final ExtensionContext.Store extensionStore = locateExtensionStore( context.getRequiredTestInstance(), context ); extensionStore.put( SESSION_FACTORY_KEY, created ); + + cleanup( context, ClearMode.BEFORE_EACH ); } private static ExtensionContext.Store locateExtensionStore(Object testInstance, ExtensionContext context) { @@ -111,6 +117,8 @@ private static SessionFactoryScopeImpl createSessionFactoryScope( DomainModelScope domainModelScope, ExtensionContext context) { SessionFactoryProducer producer = null; + ClearMode dropDataMode = ClearMode.NEVER; + ClearMode clearCacheMode = ClearMode.NEVER; if ( testInstance instanceof SessionFactoryProducer ) { producer = (SessionFactoryProducer) testInstance; @@ -122,6 +130,8 @@ private static SessionFactoryScopeImpl createSessionFactoryScope( if ( sfAnnRef.isPresent() ) { final SessionFactory sessionFactoryConfig = sfAnnRef.get(); + dropDataMode = sessionFactoryConfig.dropData(); + clearCacheMode = sessionFactoryConfig.clearCache(); producer = model -> { try { @@ -166,7 +176,7 @@ else if ( ! explicitInspectorClass.equals( StatementInspector.class ) ) { throw new IllegalStateException( "Could not determine SessionFactory producer" ); } - final SessionFactoryScopeImpl sfScope = new SessionFactoryScopeImpl( domainModelScope, producer ); + final SessionFactoryScopeImpl sfScope = new SessionFactoryScopeImpl( domainModelScope, producer, dropDataMode, clearCacheMode ); if ( testInstance instanceof SessionFactoryScopeAware ) { ( (SessionFactoryScopeAware) testInstance ).injectSessionFactoryScope( sfScope ); @@ -232,18 +242,42 @@ public void handleTestExecutionException(ExtensionContext context, Throwable thr throw throwable; } + @Override + public void afterAll(ExtensionContext context) { + cleanup( context, ClearMode.AFTER_ALL ); + } + + @Override + public void afterEach(ExtensionContext context) { + cleanup( context, ClearMode.AFTER_EACH ); + } + + private void cleanup(ExtensionContext context, ClearMode dropDataMode) { + final Object testInstance = context.getRequiredTestInstance(); + final ExtensionContext.Store store = locateExtensionStore( testInstance, context ); + final SessionFactoryScopeImpl scope = (SessionFactoryScopeImpl) store.get( SESSION_FACTORY_KEY ); + if (scope != null ) { + scope.dropData( dropDataMode ); + scope.clearCache( dropDataMode ); + } + } + private static class SessionFactoryScopeImpl implements SessionFactoryScope, ExtensionContext.Store.CloseableResource { private final DomainModelScope modelScope; private final SessionFactoryProducer producer; + private final ClearMode dropDataMode; + private final ClearMode clearCacheMode; private SessionFactoryImplementor sessionFactory; private boolean active = true; private SessionFactoryScopeImpl( DomainModelScope modelScope, - SessionFactoryProducer producer) { + SessionFactoryProducer producer, ClearMode dropDataMode, ClearMode clearCacheMode) { this.modelScope = modelScope; this.producer = producer; + this.dropDataMode = dropDataMode; + this.clearCacheMode = clearCacheMode; } @Override @@ -416,5 +450,17 @@ public void dropData() { sessionFactory.getSchemaManager().truncateMappedObjects(); } } + + void dropData(ClearMode dropDataMode) { + if ( this.dropDataMode == dropDataMode ) { + dropData(); + } + } + + void clearCache(ClearMode clearCacheMode) { + if ( this.clearCacheMode == clearCacheMode ) { + sessionFactory.getCache().evictAllRegions(); + } + } } }