From a898ad7b4a8bb42731fb0a6d7c4ca6135ba4e26e Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Wed, 12 Nov 2025 09:54:08 +0100 Subject: [PATCH 1/2] Switch from the Search session holder to an ORM session extension --- build/config/pom.xml | 3 + build/parents/build/pom.xml | 2 +- ...fterCommitIndexingPlanSynchronization.java | 8 +- ...foreCommitIndexingPlanSynchronization.java | 8 +- .../ConfiguredAutomaticIndexingStrategy.java | 6 +- .../impl/HibernateOrmSearchSession.java | 23 ++-- .../HibernateOrmSearchSessionExtension.java | 57 +++++++++ .../impl/HibernateOrmSearchSessionHolder.java | 111 ------------------ ...nateSearchSessionExtensionIntegration.java | 20 ++++ ....engine.extension.spi.ExtensionIntegration | 1 + 10 files changed, 102 insertions(+), 137 deletions(-) create mode 100644 mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/HibernateOrmSearchSessionExtension.java delete mode 100644 mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/HibernateOrmSearchSessionHolder.java create mode 100644 mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/HibernateSearchSessionExtensionIntegration.java create mode 100644 mapper/orm/src/main/resources/META-INF/services/org.hibernate.engine.extension.spi.ExtensionIntegration diff --git a/build/config/pom.xml b/build/config/pom.xml index ba893e98e25..00e5e40298b 100644 --- a/build/config/pom.xml +++ b/build/config/pom.xml @@ -248,6 +248,9 @@ ^org\.hibernate\.query\.spi\.QueryParameterBindingTypeResolver$ ^org\.hibernate\.query\.sqm\.spi\.SqmCreationContext$ ^org\.hibernate\.sql\.results\.graph\.Fetchable$ + + ^org\.hibernate\.engine\.extension\.spi\.[a-zA-Z]+$ + ^org\.hibernate\.engine\.spi\.SharedSessionContractImplementor#getExtension.+$ - 7.2.0.CR2 + 7.2.0-SNAPSHOT https://docs.jboss.org/hibernate/orm/${parsed-version.org.hibernate.orm.majorVersion}.${parsed-version.org.hibernate.orm.minorVersion}/javadocs/ https://docs.jboss.org/hibernate/orm/${parsed-version.org.hibernate.orm.majorVersion}.${parsed-version.org.hibernate.orm.minorVersion}/userguide/html_single/Hibernate_User_Guide.html diff --git a/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/AfterCommitIndexingPlanSynchronization.java b/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/AfterCommitIndexingPlanSynchronization.java index 1309380443c..d58303348e3 100644 --- a/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/AfterCommitIndexingPlanSynchronization.java +++ b/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/AfterCommitIndexingPlanSynchronization.java @@ -18,15 +18,15 @@ class AfterCommitIndexingPlanSynchronization implements Synchronization { private final PojoIndexingPlan indexingPlan; - private final HibernateOrmSearchSessionHolder sessionHolder; + private final HibernateOrmSearchSessionExtension sessionExtension; private final Transaction transactionIdentifier; private final ConfiguredIndexingPlanSynchronizationStrategy synchronizationStrategy; AfterCommitIndexingPlanSynchronization(PojoIndexingPlan indexingPlan, - HibernateOrmSearchSessionHolder sessionHolder, Transaction transactionIdentifier, + HibernateOrmSearchSessionExtension sessionExtension, Transaction transactionIdentifier, ConfiguredIndexingPlanSynchronizationStrategy synchronizationStrategy) { this.indexingPlan = indexingPlan; - this.sessionHolder = sessionHolder; + this.sessionExtension = sessionExtension; this.transactionIdentifier = transactionIdentifier; this.synchronizationStrategy = synchronizationStrategy; } @@ -51,7 +51,7 @@ public void afterCompletion(int i) { } finally { //clean the Synchronization per Transaction - sessionHolder.clear( transactionIdentifier ); + sessionExtension.clear( transactionIdentifier ); } } } diff --git a/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/BeforeCommitIndexingPlanSynchronization.java b/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/BeforeCommitIndexingPlanSynchronization.java index b5a6f168400..3a27e781368 100644 --- a/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/BeforeCommitIndexingPlanSynchronization.java +++ b/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/BeforeCommitIndexingPlanSynchronization.java @@ -18,15 +18,15 @@ class BeforeCommitIndexingPlanSynchronization implements Synchronization { private final PojoIndexingPlan indexingPlan; - private final HibernateOrmSearchSessionHolder sessionHolder; + private final HibernateOrmSearchSessionExtension sessionExtension; private final Transaction transactionIdentifier; private final ConfiguredIndexingPlanSynchronizationStrategy synchronizationStrategy; BeforeCommitIndexingPlanSynchronization(PojoIndexingPlan indexingPlan, - HibernateOrmSearchSessionHolder sessionHolder, Transaction transactionIdentifier, + HibernateOrmSearchSessionExtension sessionExtension, Transaction transactionIdentifier, ConfiguredIndexingPlanSynchronizationStrategy synchronizationStrategy) { this.indexingPlan = indexingPlan; - this.sessionHolder = sessionHolder; + this.sessionExtension = sessionExtension; this.transactionIdentifier = transactionIdentifier; this.synchronizationStrategy = synchronizationStrategy; } @@ -47,7 +47,7 @@ public void afterCompletion(int i) { } finally { //clean the Synchronization per Transaction - sessionHolder.clear( transactionIdentifier ); + sessionExtension.clear( transactionIdentifier ); } } } diff --git a/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/ConfiguredAutomaticIndexingStrategy.java b/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/ConfiguredAutomaticIndexingStrategy.java index 42753b89165..7eb3a60144a 100644 --- a/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/ConfiguredAutomaticIndexingStrategy.java +++ b/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/ConfiguredAutomaticIndexingStrategy.java @@ -238,15 +238,15 @@ public PojoIndexingPlan createIndexingPlan(HibernateOrmSearchSession context, } public Synchronization createTransactionWorkQueueSynchronization(PojoIndexingPlan indexingPlan, - HibernateOrmSearchSessionHolder sessionProperties, + HibernateOrmSearchSessionExtension sessionExtension, Transaction transactionIdentifier, ConfiguredIndexingPlanSynchronizationStrategy synchronizationStrategy) { if ( enlistsInTransaction ) { - return new BeforeCommitIndexingPlanSynchronization( indexingPlan, sessionProperties, transactionIdentifier, + return new BeforeCommitIndexingPlanSynchronization( indexingPlan, sessionExtension, transactionIdentifier, synchronizationStrategy ); } else { - return new AfterCommitIndexingPlanSynchronization( indexingPlan, sessionProperties, transactionIdentifier, + return new AfterCommitIndexingPlanSynchronization( indexingPlan, sessionExtension, transactionIdentifier, synchronizationStrategy ); } } diff --git a/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/HibernateOrmSearchSession.java b/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/HibernateOrmSearchSession.java index 2d158bd1020..e73660f6ac4 100644 --- a/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/HibernateOrmSearchSession.java +++ b/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/HibernateOrmSearchSession.java @@ -76,13 +76,8 @@ public static HibernateOrmSearchSession get(HibernateOrmSearchSessionMappingCont */ public static HibernateOrmSearchSession get(HibernateOrmSearchSessionMappingContext context, SessionImplementor sessionImplementor, boolean createIfDoesNotExist) { - HibernateOrmSearchSessionHolder holder = - HibernateOrmSearchSessionHolder.get( sessionImplementor, createIfDoesNotExist ); - if ( holder == null ) { - // Can only happen if createIfDoesNotExist is false - return null; - } - HibernateOrmSearchSession searchSession = holder.searchSession(); + HibernateOrmSearchSessionExtension extension = HibernateOrmSearchSessionExtension.get( sessionImplementor ); + HibernateOrmSearchSession searchSession = extension.searchSession(); if ( searchSession != null ) { return searchSession; } @@ -92,7 +87,7 @@ public static HibernateOrmSearchSession get(HibernateOrmSearchSessionMappingCont } searchSession = context.createSessionBuilder( sessionImplementor ).build(); - holder.searchSession( searchSession ); + extension.searchSession( searchSession ); return searchSession; } @@ -276,9 +271,9 @@ public PojoRuntimeIntrospector runtimeIntrospector() { @Override public PojoIndexingPlan currentIndexingPlan(boolean createIfDoesNotExist) { - HibernateOrmSearchSessionHolder holder = - HibernateOrmSearchSessionHolder.get( sessionImplementor, createIfDoesNotExist ); - if ( holder == null ) { + HibernateOrmSearchSessionExtension extension = + HibernateOrmSearchSessionExtension.get( sessionImplementor ); + if ( extension.searchSession() == null ) { // Can only happen if createIfDoesNotExist is false return null; } @@ -291,7 +286,7 @@ public PojoIndexingPlan currentIndexingPlan(boolean createIfDoesNotExist) { transactionIdentifier = null; } - PojoIndexingPlan plan = holder.pojoIndexingPlan( transactionIdentifier ); + PojoIndexingPlan plan = extension.pojoIndexingPlan( transactionIdentifier ); if ( plan != null ) { return plan; } @@ -303,11 +298,11 @@ public PojoIndexingPlan currentIndexingPlan(boolean createIfDoesNotExist) { ConfiguredIndexingPlanSynchronizationStrategy currentSynchronizationStrategy = indexingPlanSynchronizationStrategy; plan = automaticIndexingStrategy.createIndexingPlan( this, currentSynchronizationStrategy ); - holder.pojoIndexingPlan( transactionIdentifier, plan ); + extension.pojoIndexingPlan( transactionIdentifier, plan ); if ( sessionImplementor.isTransactionInProgress() ) { Synchronization txSync = automaticIndexingStrategy.createTransactionWorkQueueSynchronization( - plan, holder, transactionIdentifier, + plan, extension, transactionIdentifier, currentSynchronizationStrategy ); registerSynchronization( sessionImplementor, txSync ); diff --git a/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/HibernateOrmSearchSessionExtension.java b/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/HibernateOrmSearchSessionExtension.java new file mode 100644 index 00000000000..78763157e00 --- /dev/null +++ b/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/HibernateOrmSearchSessionExtension.java @@ -0,0 +1,57 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.search.mapper.orm.session.impl; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import org.hibernate.Transaction; +import org.hibernate.engine.extension.spi.Extension; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.search.mapper.pojo.work.spi.PojoIndexingPlan; + +public class HibernateOrmSearchSessionExtension implements Serializable, Extension { + + public static HibernateOrmSearchSessionExtension get(SessionImplementor session) { + return session.getExtension( HibernateOrmSearchSessionExtension.class ); + } + + // Everything here should be transient because the holder might get serialized along with a Hibernate ORM session. + // The Hibernate Search data (indexing plans in particular) will be lost in the process, + // but that's the best we can do. + private transient HibernateOrmSearchSession searchSession; + private transient Map planPerTransaction; + + public static HibernateOrmSearchSessionExtension init() { + return new HibernateOrmSearchSessionExtension(); + } + + public HibernateOrmSearchSession searchSession() { + return searchSession; + } + + public void searchSession(HibernateOrmSearchSession searchSession) { + this.searchSession = searchSession; + } + + public PojoIndexingPlan pojoIndexingPlan(Transaction transaction) { + return planPerTransaction == null ? null : planPerTransaction.get( transaction ); + } + + public void pojoIndexingPlan(Transaction transaction, PojoIndexingPlan plan) { + if ( planPerTransaction == null ) { + planPerTransaction = new HashMap<>(); + } + planPerTransaction.put( transaction, plan ); + } + + public void clear(Transaction transactionIdentifier) { + if ( planPerTransaction == null ) { + return; + } + planPerTransaction.remove( transactionIdentifier ); + } +} diff --git a/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/HibernateOrmSearchSessionHolder.java b/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/HibernateOrmSearchSessionHolder.java deleted file mode 100644 index 42b770c713c..00000000000 --- a/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/HibernateOrmSearchSessionHolder.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.search.mapper.orm.session.impl; - -import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import jakarta.transaction.Synchronization; - -import org.hibernate.Transaction; -import org.hibernate.engine.spi.SessionImplementor; -import org.hibernate.search.mapper.pojo.work.spi.PojoIndexingPlan; - -public class HibernateOrmSearchSessionHolder implements Serializable { - - private static final String SESSION_PROPERTY_KEY = "hibernate.search.session"; - - private static final Map holderPerClosedSessionTransaction = - new ConcurrentHashMap<>(); - - // Public for tests only - public static int staticMapSize() { - return holderPerClosedSessionTransaction.size(); - } - - public static HibernateOrmSearchSessionHolder get(SessionImplementor session, boolean createIfMissing) { - HibernateOrmSearchSessionHolder holder = - (HibernateOrmSearchSessionHolder) session.getProperties().get( SESSION_PROPERTY_KEY ); - if ( holder != null ) { - return holder; - } - boolean closedSessionAndTransaction = session.isClosed() && session.isTransactionInProgress(); - if ( closedSessionAndTransaction ) { - // This can happen when using JTA. - // Only do this as a fallback: if somehow the session holder was added to the session properties - // before the session was closed, we definitely want to use it and avoid the static map. - holder = holderPerClosedSessionTransaction.get( session.accessTransaction() ); - } - if ( holder != null ) { - return holder; - } - if ( !createIfMissing ) { - return null; - } - holder = new HibernateOrmSearchSessionHolder(); - if ( closedSessionAndTransaction ) { - // This can happen when using JTA. - Transaction transaction = session.accessTransaction(); - transaction.registerSynchronization( new HolderPerClosedSessionTransactionCleanup( transaction ) ); - holderPerClosedSessionTransaction.put( transaction, holder ); - } - else { - session.setProperty( SESSION_PROPERTY_KEY, holder ); - } - return holder; - } - - // Everything here should be transient because the holder might get serialized along with a Hibernate ORM session. - // The Hibernate Search data (indexing plans in particular) will be lost in the process, - // but that's the best we can do. - private transient HibernateOrmSearchSession searchSession; - private transient Map planPerTransaction; - - public HibernateOrmSearchSession searchSession() { - return searchSession; - } - - public void searchSession(HibernateOrmSearchSession searchSession) { - this.searchSession = searchSession; - } - - public PojoIndexingPlan pojoIndexingPlan(Transaction transaction) { - return planPerTransaction == null ? null : planPerTransaction.get( transaction ); - } - - public void pojoIndexingPlan(Transaction transaction, PojoIndexingPlan plan) { - if ( planPerTransaction == null ) { - planPerTransaction = new HashMap<>(); - } - planPerTransaction.put( transaction, plan ); - } - - public void clear(Transaction transactionIdentifier) { - if ( planPerTransaction == null ) { - return; - } - planPerTransaction.remove( transactionIdentifier ); - } - - private static class HolderPerClosedSessionTransactionCleanup implements Synchronization { - private final Transaction transaction; - - public HolderPerClosedSessionTransactionCleanup(Transaction transaction) { - this.transaction = transaction; - } - - @Override - public void beforeCompletion() { - // Nothing to do - } - - @Override - public void afterCompletion(int i) { - holderPerClosedSessionTransaction.remove( transaction ); - } - } -} diff --git a/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/HibernateSearchSessionExtensionIntegration.java b/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/HibernateSearchSessionExtensionIntegration.java new file mode 100644 index 00000000000..fa59cdc9a4a --- /dev/null +++ b/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/HibernateSearchSessionExtensionIntegration.java @@ -0,0 +1,20 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.search.mapper.orm.session.impl; + +import org.hibernate.engine.extension.spi.ExtensionIntegration; +import org.hibernate.engine.extension.spi.ExtensionIntegrationContext; + +public class HibernateSearchSessionExtensionIntegration implements ExtensionIntegration { + @Override + public Class getExtensionType() { + return HibernateOrmSearchSessionExtension.class; + } + + @Override + public HibernateOrmSearchSessionExtension createExtension(ExtensionIntegrationContext extensionIntegrationContext) { + return HibernateOrmSearchSessionExtension.init(); + } +} diff --git a/mapper/orm/src/main/resources/META-INF/services/org.hibernate.engine.extension.spi.ExtensionIntegration b/mapper/orm/src/main/resources/META-INF/services/org.hibernate.engine.extension.spi.ExtensionIntegration new file mode 100644 index 00000000000..8fa058c7029 --- /dev/null +++ b/mapper/orm/src/main/resources/META-INF/services/org.hibernate.engine.extension.spi.ExtensionIntegration @@ -0,0 +1 @@ +org.hibernate.search.mapper.orm.session.impl.HibernateSearchSessionExtensionIntegration From c219d341a4cbe19802589fc52e3c2899b539a847 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Mon, 17 Nov 2025 19:08:09 +0100 Subject: [PATCH 2/2] A few more updates for ORM session extension --- .../spring/jta/JtaAndSpringIT.java | 7 ---- .../spring/jta/JtaAndSpringMoreComplexIT.java | 7 ---- .../spring/jta/JtaAndSpringOutboxIT.java | 7 ---- .../orm/mapping/impl/HibernateOrmMapping.java | 5 +-- .../impl/HibernateOrmSearchSession.java | 38 +++++++++++++------ .../HibernateOrmSearchSessionExtension.java | 4 +- 6 files changed, 31 insertions(+), 37 deletions(-) diff --git a/integrationtest/mapper/orm-spring/src/test/java/org/hibernate/search/integrationtest/spring/jta/JtaAndSpringIT.java b/integrationtest/mapper/orm-spring/src/test/java/org/hibernate/search/integrationtest/spring/jta/JtaAndSpringIT.java index 365e779de7f..d35a596ba22 100644 --- a/integrationtest/mapper/orm-spring/src/test/java/org/hibernate/search/integrationtest/spring/jta/JtaAndSpringIT.java +++ b/integrationtest/mapper/orm-spring/src/test/java/org/hibernate/search/integrationtest/spring/jta/JtaAndSpringIT.java @@ -13,11 +13,9 @@ import org.hibernate.search.integrationtest.spring.jta.dao.SnertDAO; import org.hibernate.search.integrationtest.spring.jta.entity.Snert; import org.hibernate.search.integrationtest.spring.testsupport.AbstractMapperOrmSpringIT; -import org.hibernate.search.mapper.orm.session.impl.HibernateOrmSearchSessionHolder; import org.hibernate.search.util.impl.integrationtest.common.extension.BackendMock; import org.hibernate.search.util.impl.test.annotation.PortedFromSearch5; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -50,11 +48,6 @@ void checkJta() { .returns( true, TransactionCoordinatorBuilder::isJta ); } - @AfterEach - void checkNoMemoryLeak() { - assertThat( HibernateOrmSearchSessionHolder.staticMapSize() ).isZero(); - } - @Test void test() { Snert snert = new Snert(); diff --git a/integrationtest/mapper/orm-spring/src/test/java/org/hibernate/search/integrationtest/spring/jta/JtaAndSpringMoreComplexIT.java b/integrationtest/mapper/orm-spring/src/test/java/org/hibernate/search/integrationtest/spring/jta/JtaAndSpringMoreComplexIT.java index fb683b2222b..3f800e2bee4 100644 --- a/integrationtest/mapper/orm-spring/src/test/java/org/hibernate/search/integrationtest/spring/jta/JtaAndSpringMoreComplexIT.java +++ b/integrationtest/mapper/orm-spring/src/test/java/org/hibernate/search/integrationtest/spring/jta/JtaAndSpringMoreComplexIT.java @@ -15,11 +15,9 @@ import org.hibernate.search.integrationtest.spring.jta.entity.Doughnut; import org.hibernate.search.integrationtest.spring.jta.entity.Muffin; import org.hibernate.search.integrationtest.spring.testsupport.AbstractMapperOrmSpringIT; -import org.hibernate.search.mapper.orm.session.impl.HibernateOrmSearchSessionHolder; import org.hibernate.search.util.impl.integrationtest.common.extension.BackendMock; import org.hibernate.search.util.impl.test.annotation.PortedFromSearch5; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -52,11 +50,6 @@ void checkJta() { .returns( true, TransactionCoordinatorBuilder::isJta ); } - @AfterEach - void checkNoMemoryLeak() { - assertThat( HibernateOrmSearchSessionHolder.staticMapSize() ).isZero(); - } - @Test void testMuffins() { Box box = new Box(); diff --git a/integrationtest/mapper/orm-spring/src/test/java/org/hibernate/search/integrationtest/spring/jta/JtaAndSpringOutboxIT.java b/integrationtest/mapper/orm-spring/src/test/java/org/hibernate/search/integrationtest/spring/jta/JtaAndSpringOutboxIT.java index 321dbab9d50..62df6de8b58 100644 --- a/integrationtest/mapper/orm-spring/src/test/java/org/hibernate/search/integrationtest/spring/jta/JtaAndSpringOutboxIT.java +++ b/integrationtest/mapper/orm-spring/src/test/java/org/hibernate/search/integrationtest/spring/jta/JtaAndSpringOutboxIT.java @@ -13,10 +13,8 @@ import org.hibernate.search.integrationtest.spring.jta.dao.SnertDAO; import org.hibernate.search.integrationtest.spring.jta.entity.Snert; import org.hibernate.search.integrationtest.spring.testsupport.AbstractMapperOrmSpringIT; -import org.hibernate.search.mapper.orm.session.impl.HibernateOrmSearchSessionHolder; import org.hibernate.search.util.impl.integrationtest.common.extension.BackendMock; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -48,11 +46,6 @@ void checkJta() { .returns( true, TransactionCoordinatorBuilder::isJta ); } - @AfterEach - void checkNoMemoryLeak() { - assertThat( HibernateOrmSearchSessionHolder.staticMapSize() ).isZero(); - } - @Test void test() { Snert snert = new Snert(); diff --git a/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/mapping/impl/HibernateOrmMapping.java b/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/mapping/impl/HibernateOrmMapping.java index e5ae46217b1..1fbe2de3f99 100644 --- a/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/mapping/impl/HibernateOrmMapping.java +++ b/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/mapping/impl/HibernateOrmMapping.java @@ -377,10 +377,9 @@ public PojoIndexingPlan currentIndexingPlanIfExisting(SessionImplementor session @Override public PojoTypeIndexingPlan currentIndexingPlanIfTypeIncluded( - SharedSessionContractImplementor sessionImplementor, + SharedSessionContractImplementor session, PojoRawTypeIdentifier typeIdentifier) { try { - SessionImplementor session = sessionImplementor.unwrap( SessionImplementor.class ); HibernateOrmSearchSession searchSession = HibernateOrmSearchSession.get( this, session, false ); if ( searchSession != null ) { // If the session exist, rely on the session-level filter @@ -403,7 +402,7 @@ public PojoTypeIndexingPlan currentIndexingPlanIfTypeIncluded( } } catch (PersistenceException e) { - throw OrmMiscLog.INSTANCE.unsupportedSessionType( sessionImplementor.getClass() ); + throw OrmMiscLog.INSTANCE.unsupportedSessionType( session.getClass() ); } } diff --git a/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/HibernateOrmSearchSession.java b/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/HibernateOrmSearchSession.java index e73660f6ac4..6a96d0002e8 100644 --- a/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/HibernateOrmSearchSession.java +++ b/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/HibernateOrmSearchSession.java @@ -17,6 +17,7 @@ import org.hibernate.action.spi.BeforeTransactionCompletionProcess; import org.hibernate.engine.spi.ActionQueue; import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.search.engine.backend.common.spi.EntityReferenceFactory; import org.hibernate.search.engine.search.common.NonStaticMetamodelScope; import org.hibernate.search.engine.search.query.dsl.SearchQuerySelectStep; @@ -66,7 +67,7 @@ public class HibernateOrmSearchSession extends AbstractPojoSearchSession * @return The {@link HibernateOrmSearchSession} to use within the context of the given session. */ public static HibernateOrmSearchSession get(HibernateOrmSearchSessionMappingContext context, - SessionImplementor sessionImplementor) { + SharedSessionContractImplementor sessionImplementor) { return get( context, sessionImplementor, true ); } @@ -75,7 +76,7 @@ public static HibernateOrmSearchSession get(HibernateOrmSearchSessionMappingCont * @return The {@link HibernateOrmSearchSession} to use within the context of the given session. */ public static HibernateOrmSearchSession get(HibernateOrmSearchSessionMappingContext context, - SessionImplementor sessionImplementor, boolean createIfDoesNotExist) { + SharedSessionContractImplementor sessionImplementor, boolean createIfDoesNotExist) { HibernateOrmSearchSessionExtension extension = HibernateOrmSearchSessionExtension.get( sessionImplementor ); HibernateOrmSearchSession searchSession = extension.searchSession(); if ( searchSession != null ) { @@ -86,9 +87,16 @@ public static HibernateOrmSearchSession get(HibernateOrmSearchSessionMappingCont return null; } - searchSession = context.createSessionBuilder( sessionImplementor ).build(); - extension.searchSession( searchSession ); - return searchSession; + if ( sessionImplementor instanceof SessionImplementor implementor ) { + searchSession = context.createSessionBuilder( implementor ).build(); + extension.searchSession( searchSession ); + return searchSession; + } + else { + // TODO: For a stateless session need a different impl of the search session + throw new UnsupportedOperationException( + "Cannot create Search Session using a non stateful ORM session: " + sessionImplementor.getClass() ); + } } private final HibernateOrmSearchSessionMappingContext mappingContext; @@ -198,6 +206,10 @@ public TypedSearchScope typedScope(Class rootScope, Collectio return mappingContext.createScope( rootScope, classes ); } + // TODO: won't be able to use these (toEntityManager / toOrmSession / session) in a stateless session case... + // options ? + // = deprecate and introduce some shared session interface instead ? + // = keep two search session interfaces one for stateless one for stateful ? @Override public EntityManager toEntityManager() { return sessionImplementor; @@ -208,6 +220,11 @@ public Session toOrmSession() { return sessionImplementor; } + @Override + public SessionImplementor session() { + return sessionImplementor; + } + @Override public SearchIndexingPlan indexingPlan() { if ( indexingPlan == null ) { @@ -249,11 +266,6 @@ public ConfiguredSearchIndexingPlanFilter configuredIndexingPlanFilter() { return configuredIndexingPlanFilter; } - @Override - public SessionImplementor session() { - return sessionImplementor; - } - @Override public EntityReferenceFactory entityReferenceFactory() { return mappingContext.entityReferenceFactory(); @@ -342,6 +354,10 @@ private void registerSynchronization(SessionImplementor sessionImplementor, Sync * In a JTA env, the before transaction completion is called before the flush, so not all changes are yet * written. However, Synchronization-s do propagate exceptions, so they can be safely used. */ + + // TODO: There's no action queue with stateless session ... + // Hence it would be great if we could just have an SPI in ORM to deal with this instead? + // TODO: Have a closer look at this part to figure the best way to deal with stateless sessions.. final ActionQueue actionQueue = sessionImplementor.getActionQueue(); SynchronizationAdapter adapter = new SynchronizationAdapter( synchronization ); @@ -367,7 +383,7 @@ private boolean isLocalTransaction(SessionImplementor sessionImplementor) { .isJta(); } - private static void checkOpen(SessionImplementor session) { + private static void checkOpen(SharedSessionContractImplementor session) { try { session.checkOpen(); } diff --git a/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/HibernateOrmSearchSessionExtension.java b/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/HibernateOrmSearchSessionExtension.java index 78763157e00..9a582eedc00 100644 --- a/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/HibernateOrmSearchSessionExtension.java +++ b/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/impl/HibernateOrmSearchSessionExtension.java @@ -10,12 +10,12 @@ import org.hibernate.Transaction; import org.hibernate.engine.extension.spi.Extension; -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.search.mapper.pojo.work.spi.PojoIndexingPlan; public class HibernateOrmSearchSessionExtension implements Serializable, Extension { - public static HibernateOrmSearchSessionExtension get(SessionImplementor session) { + public static HibernateOrmSearchSessionExtension get(SharedSessionContractImplementor session) { return session.getExtension( HibernateOrmSearchSessionExtension.class ); }