HHH-17619 Add the multitenancy filter in a stateless session
This commit is contained in:
parent
f5800a0388
commit
733b555e86
|
@ -29,7 +29,9 @@ import org.hibernate.SessionEventListener;
|
||||||
import org.hibernate.SessionException;
|
import org.hibernate.SessionException;
|
||||||
import org.hibernate.Transaction;
|
import org.hibernate.Transaction;
|
||||||
import org.hibernate.UnknownEntityTypeException;
|
import org.hibernate.UnknownEntityTypeException;
|
||||||
|
import org.hibernate.binder.internal.TenantIdBinder;
|
||||||
import org.hibernate.cache.spi.CacheTransactionSynchronization;
|
import org.hibernate.cache.spi.CacheTransactionSynchronization;
|
||||||
|
import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
|
||||||
import org.hibernate.engine.internal.SessionEventListenerManagerImpl;
|
import org.hibernate.engine.internal.SessionEventListenerManagerImpl;
|
||||||
import org.hibernate.engine.jdbc.LobCreator;
|
import org.hibernate.engine.jdbc.LobCreator;
|
||||||
import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess;
|
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.jdbc.spi.JdbcServices;
|
||||||
import org.hibernate.engine.spi.EntityKey;
|
import org.hibernate.engine.spi.EntityKey;
|
||||||
import org.hibernate.engine.spi.ExceptionConverter;
|
import org.hibernate.engine.spi.ExceptionConverter;
|
||||||
|
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||||
import org.hibernate.engine.spi.SessionEventListenerManager;
|
import org.hibernate.engine.spi.SessionEventListenerManager;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
|
@ -224,6 +227,24 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
|
||||||
&& ( (SharedSessionCreationOptions) options ).isTransactionCoordinatorShared();
|
&& ( (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) {
|
private void logInconsistentOptions(SharedSessionCreationOptions sharedOptions) {
|
||||||
if ( sharedOptions.shouldAutoJoinTransactions() ) {
|
if ( sharedOptions.shouldAutoJoinTransactions() ) {
|
||||||
log.debug(
|
log.debug(
|
||||||
|
|
|
@ -49,9 +49,7 @@ import org.hibernate.TransientObjectException;
|
||||||
import org.hibernate.TypeMismatchException;
|
import org.hibernate.TypeMismatchException;
|
||||||
import org.hibernate.UnknownProfileException;
|
import org.hibernate.UnknownProfileException;
|
||||||
import org.hibernate.UnresolvableObjectException;
|
import org.hibernate.UnresolvableObjectException;
|
||||||
import org.hibernate.binder.internal.TenantIdBinder;
|
|
||||||
import org.hibernate.collection.spi.PersistentCollection;
|
import org.hibernate.collection.spi.PersistentCollection;
|
||||||
import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
|
|
||||||
import org.hibernate.engine.internal.StatefulPersistenceContext;
|
import org.hibernate.engine.internal.StatefulPersistenceContext;
|
||||||
import org.hibernate.engine.jdbc.LobCreator;
|
import org.hibernate.engine.jdbc.LobCreator;
|
||||||
import org.hibernate.engine.jdbc.NonContextualLobCreator;
|
import org.hibernate.engine.jdbc.NonContextualLobCreator;
|
||||||
|
@ -268,7 +266,7 @@ public class SessionImpl
|
||||||
setHibernateFlushMode( getInitialFlushMode() );
|
setHibernateFlushMode( getInitialFlushMode() );
|
||||||
}
|
}
|
||||||
|
|
||||||
setUpMultitenancy( factory );
|
setUpMultitenancy( factory, loadQueryInfluencers );
|
||||||
|
|
||||||
if ( log.isTraceEnabled() ) {
|
if ( log.isTraceEnabled() ) {
|
||||||
log.tracef( "Opened Session [%s] at timestamp: %s", getSessionIdentifier(), currentTimeMillis() );
|
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 );
|
: 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() {
|
protected StatefulPersistenceContext createPersistenceContext() {
|
||||||
return new StatefulPersistenceContext( this );
|
return new StatefulPersistenceContext( this );
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,6 +78,7 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
|
||||||
connectionProvided = options.getConnection() != null;
|
connectionProvided = options.getConnection() != null;
|
||||||
temporaryPersistenceContext = new StatefulPersistenceContext( this );
|
temporaryPersistenceContext = new StatefulPersistenceContext( this );
|
||||||
influencers = new LoadQueryInfluencers( getFactory() );
|
influencers = new LoadQueryInfluencers( getFactory() );
|
||||||
|
setUpMultitenancy( factory, influencers );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -8,6 +8,7 @@ package org.hibernate.orm.test.tenantid;
|
||||||
|
|
||||||
import org.hibernate.HibernateError;
|
import org.hibernate.HibernateError;
|
||||||
import org.hibernate.PropertyValueException;
|
import org.hibernate.PropertyValueException;
|
||||||
|
import org.hibernate.StatelessSession;
|
||||||
import org.hibernate.boot.SessionFactoryBuilder;
|
import org.hibernate.boot.SessionFactoryBuilder;
|
||||||
import org.hibernate.boot.spi.MetadataImplementor;
|
import org.hibernate.boot.spi.MetadataImplementor;
|
||||||
import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
|
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.SessionFactoryScope;
|
||||||
import org.hibernate.testing.orm.junit.Setting;
|
import org.hibernate.testing.orm.junit.Setting;
|
||||||
import org.hibernate.binder.internal.TenantIdBinder;
|
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.hibernate.testing.orm.junit.SkipForDialect;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
@ -28,6 +32,7 @@ import org.junit.jupiter.api.Test;
|
||||||
import jakarta.persistence.EntityManager;
|
import jakarta.persistence.EntityManager;
|
||||||
import jakarta.persistence.EntityManagerFactory;
|
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.cfg.AvailableSettings.JAKARTA_HBM2DDL_DATABASE_ACTION;
|
||||||
import static org.hibernate.internal.util.collections.CollectionHelper.toMap;
|
import static org.hibernate.internal.util.collections.CollectionHelper.toMap;
|
||||||
import static org.hibernate.jpa.HibernateHints.HINT_TENANT_ID;
|
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.assertTrue;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@SessionFactory
|
@SessionFactory
|
||||||
@DomainModel(annotatedClasses = { Account.class, Client.class, Record.class })
|
@DomainModel(annotatedClasses = { Account.class, Client.class, Record.class })
|
||||||
@ServiceRegistry(
|
@ServiceRegistry(
|
||||||
|
@ -53,6 +60,7 @@ public class TenantIdTest implements SessionFactoryProducer {
|
||||||
scope.inTransaction( session -> {
|
scope.inTransaction( session -> {
|
||||||
session.createQuery("delete from Account").executeUpdate();
|
session.createQuery("delete from Account").executeUpdate();
|
||||||
session.createQuery("delete from Client").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() {
|
private static void waitALittle() {
|
||||||
try {
|
try {
|
||||||
Thread.sleep( 10 );
|
Thread.sleep( 10 );
|
||||||
|
|
Loading…
Reference in New Issue