diff --git a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java index c481be634d..3a32d26ddf 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java @@ -29,7 +29,9 @@ import org.hibernate.SessionEventListener; import org.hibernate.SessionException; import org.hibernate.Transaction; import org.hibernate.UnknownEntityTypeException; +import org.hibernate.binder.internal.TenantIdBinder; import org.hibernate.cache.spi.CacheTransactionSynchronization; +import org.hibernate.context.spi.CurrentTenantIdentifierResolver; import org.hibernate.engine.internal.SessionEventListenerManagerImpl; import org.hibernate.engine.jdbc.LobCreator; import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; @@ -38,6 +40,7 @@ import org.hibernate.engine.jdbc.spi.JdbcCoordinator; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.ExceptionConverter; +import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.SessionEventListenerManager; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; @@ -224,6 +227,24 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont && ( (SharedSessionCreationOptions) options ).isTransactionCoordinatorShared(); } + protected final void setUpMultitenancy(SessionFactoryImplementor factory, LoadQueryInfluencers loadQueryInfluencers) { + if ( factory.getDefinedFilterNames().contains( TenantIdBinder.FILTER_NAME ) ) { + final Object tenantIdentifier = getTenantIdentifierValue(); + if ( tenantIdentifier == null ) { + throw new HibernateException( "SessionFactory configured for multi-tenancy, but no tenant identifier specified" ); + } + else { + final CurrentTenantIdentifierResolver resolver = factory.getCurrentTenantIdentifierResolver(); + if ( resolver==null || !resolver.isRoot( tenantIdentifier ) ) { + // turn on the filter, unless this is the "root" tenant with access to all partitions + loadQueryInfluencers + .enableFilter( TenantIdBinder.FILTER_NAME ) + .setParameter( TenantIdBinder.PARAMETER_NAME, tenantIdentifier ); + } + } + } + } + private void logInconsistentOptions(SharedSessionCreationOptions sharedOptions) { if ( sharedOptions.shouldAutoJoinTransactions() ) { log.debug( diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java index 2cb368a851..b202d1d00e 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -49,9 +49,7 @@ import org.hibernate.TransientObjectException; import org.hibernate.TypeMismatchException; import org.hibernate.UnknownProfileException; import org.hibernate.UnresolvableObjectException; -import org.hibernate.binder.internal.TenantIdBinder; import org.hibernate.collection.spi.PersistentCollection; -import org.hibernate.context.spi.CurrentTenantIdentifierResolver; import org.hibernate.engine.internal.StatefulPersistenceContext; import org.hibernate.engine.jdbc.LobCreator; import org.hibernate.engine.jdbc.NonContextualLobCreator; @@ -268,7 +266,7 @@ public class SessionImpl setHibernateFlushMode( getInitialFlushMode() ); } - setUpMultitenancy( factory ); + setUpMultitenancy( factory, loadQueryInfluencers ); if ( log.isTraceEnabled() ) { log.tracef( "Opened Session [%s] at timestamp: %s", getSessionIdentifier(), currentTimeMillis() ); @@ -283,24 +281,6 @@ public class SessionImpl : ConfigurationHelper.getFlushMode( getSessionProperty( HINT_FLUSH_MODE ), FlushMode.AUTO ); } - private void setUpMultitenancy(SessionFactoryImplementor factory) { - if ( factory.getDefinedFilterNames().contains( TenantIdBinder.FILTER_NAME ) ) { - final Object tenantIdentifier = getTenantIdentifierValue(); - if ( tenantIdentifier == null ) { - throw new HibernateException( "SessionFactory configured for multi-tenancy, but no tenant identifier specified" ); - } - else { - final CurrentTenantIdentifierResolver resolver = factory.getCurrentTenantIdentifierResolver(); - if ( resolver==null || !resolver.isRoot( tenantIdentifier ) ) { - // turn on the filter, unless this is the "root" tenant with access to all partitions - loadQueryInfluencers - .enableFilter( TenantIdBinder.FILTER_NAME ) - .setParameter( TenantIdBinder.PARAMETER_NAME, tenantIdentifier ); - } - } - } - } - protected StatefulPersistenceContext createPersistenceContext() { return new StatefulPersistenceContext( this ); } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java index 45d137630d..f03b33389f 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java @@ -78,6 +78,7 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen connectionProvided = options.getConnection() != null; temporaryPersistenceContext = new StatefulPersistenceContext( this ); influencers = new LoadQueryInfluencers( getFactory() ); + setUpMultitenancy( factory, influencers ); } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/tenantid/TenantIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/tenantid/TenantIdTest.java index d77ed81ca3..56dce0aad8 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/tenantid/TenantIdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/tenantid/TenantIdTest.java @@ -8,6 +8,7 @@ package org.hibernate.orm.test.tenantid; import org.hibernate.HibernateError; import org.hibernate.PropertyValueException; +import org.hibernate.StatelessSession; import org.hibernate.boot.SessionFactoryBuilder; import org.hibernate.boot.spi.MetadataImplementor; import org.hibernate.context.spi.CurrentTenantIdentifierResolver; @@ -20,6 +21,9 @@ import org.hibernate.testing.orm.junit.SessionFactoryProducer; import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.hibernate.testing.orm.junit.Setting; import org.hibernate.binder.internal.TenantIdBinder; +import org.hibernate.query.criteria.HibernateCriteriaBuilder; +import org.hibernate.query.criteria.JpaCriteriaQuery; +import org.hibernate.query.criteria.JpaRoot; import org.hibernate.testing.orm.junit.SkipForDialect; import org.junit.jupiter.api.AfterEach; @@ -28,6 +32,7 @@ import org.junit.jupiter.api.Test; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; +import static org.assertj.core.api.Assertions.assertThat; import static org.hibernate.cfg.AvailableSettings.JAKARTA_HBM2DDL_DATABASE_ACTION; import static org.hibernate.internal.util.collections.CollectionHelper.toMap; import static org.hibernate.jpa.HibernateHints.HINT_TENANT_ID; @@ -37,6 +42,8 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; +import java.util.List; + @SessionFactory @DomainModel(annotatedClasses = { Account.class, Client.class, Record.class }) @ServiceRegistry( @@ -53,6 +60,7 @@ public class TenantIdTest implements SessionFactoryProducer { scope.inTransaction( session -> { session.createQuery("delete from Account").executeUpdate(); session.createQuery("delete from Client").executeUpdate(); + session.createQuery("delete from Record").executeUpdate(); }); } @@ -217,6 +225,34 @@ public class TenantIdTest implements SessionFactoryProducer { } } + + @Test + public void tenantFilterWithStatelessSession(SessionFactoryScope scope) { + currentTenant = "mine"; + Record myRecord1 = new Record(); + Record myRecord2 = new Record(); + + scope.inTransaction( session -> { + session.persist(myRecord1); + session.persist(myRecord2); + } ); + scope.inStatelessTransaction( session -> { + assertThat( listAllRecordsForTenant( session ) ).hasSize( 2 ); + } ); + + currentTenant = "yours"; + scope.inStatelessTransaction( session -> { + assertThat( listAllRecordsForTenant( session ) ).isEmpty(); + } ); + } + + private static List listAllRecordsForTenant(StatelessSession session) { + HibernateCriteriaBuilder criteriaBuilder = session.getCriteriaBuilder(); + JpaCriteriaQuery criteriaQuery = criteriaBuilder.createQuery( Record.class ); + JpaRoot from = criteriaQuery.from( Record.class ); + return session.createQuery( criteriaQuery ).getResultList(); + } + private static void waitALittle() { try { Thread.sleep( 10 );