Skip to content

Commit

Permalink
HHH-17619 Add the multitenancy filter in a stateless session
Browse files Browse the repository at this point in the history
  • Loading branch information
marko-bekhta authored and beikov committed Jan 15, 2024
1 parent f5800a0 commit 733b555
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@
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;
Expand All @@ -38,6 +40,7 @@
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;
Expand Down Expand Up @@ -224,6 +227,24 @@ private static boolean isTransactionCoordinatorShared(SessionCreationOptions opt
&& ( (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<Object> 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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,7 @@
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;
Expand Down Expand Up @@ -268,7 +266,7 @@ public SessionImpl(SessionFactoryImpl factory, SessionCreationOptions options) {
setHibernateFlushMode( getInitialFlushMode() );
}

setUpMultitenancy( factory );
setUpMultitenancy( factory, loadQueryInfluencers );

if ( log.isTraceEnabled() ) {
log.tracef( "Opened Session [%s] at timestamp: %s", getSessionIdentifier(), currentTimeMillis() );
Expand All @@ -283,24 +281,6 @@ private FlushMode getInitialFlushMode() {
: 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<Object> 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 );
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ public StatelessSessionImpl(SessionFactoryImpl factory, SessionCreationOptions o
connectionProvided = options.getConnection() != null;
temporaryPersistenceContext = new StatefulPersistenceContext( this );
influencers = new LoadQueryInfluencers( getFactory() );
setUpMultitenancy( factory, influencers );
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

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;
Expand All @@ -20,6 +21,9 @@
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;
Expand All @@ -28,6 +32,7 @@
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;
Expand All @@ -37,6 +42,8 @@
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(
Expand All @@ -53,6 +60,7 @@ public void cleanup(SessionFactoryScope scope) {
scope.inTransaction( session -> {
session.createQuery("delete from Account").executeUpdate();
session.createQuery("delete from Client").executeUpdate();
session.createQuery("delete from Record").executeUpdate();
});
}

Expand Down Expand Up @@ -217,6 +225,34 @@ public void testEntityManagerHint(SessionFactoryScope scope) {
}
}


@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<Record> listAllRecordsForTenant(StatelessSession session) {
HibernateCriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
JpaCriteriaQuery<Record> criteriaQuery = criteriaBuilder.createQuery( Record.class );
JpaRoot<Record> from = criteriaQuery.from( Record.class );
return session.createQuery( criteriaQuery ).getResultList();
}

private static void waitALittle() {
try {
Thread.sleep( 10 );
Expand Down

0 comments on commit 733b555

Please sign in to comment.