HHH-17619 Add the multitenancy filter in a stateless session

This commit is contained in:
marko-bekhta 2024-01-04 15:58:13 +01:00 committed by Christian Beikov
parent f5800a0388
commit 733b555e86
4 changed files with 59 additions and 21 deletions

View File

@ -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<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(

View File

@ -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<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 );
}

View File

@ -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

View File

@ -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<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 );