Skip to content

Commit 733b555

Browse files
marko-bekhtabeikov
authored andcommitted
HHH-17619 Add the multitenancy filter in a stateless session
1 parent f5800a0 commit 733b555

File tree

4 files changed

+59
-21
lines changed

4 files changed

+59
-21
lines changed

hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@
2929
import org.hibernate.SessionException;
3030
import org.hibernate.Transaction;
3131
import org.hibernate.UnknownEntityTypeException;
32+
import org.hibernate.binder.internal.TenantIdBinder;
3233
import org.hibernate.cache.spi.CacheTransactionSynchronization;
34+
import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
3335
import org.hibernate.engine.internal.SessionEventListenerManagerImpl;
3436
import org.hibernate.engine.jdbc.LobCreator;
3537
import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess;
@@ -38,6 +40,7 @@
3840
import org.hibernate.engine.jdbc.spi.JdbcServices;
3941
import org.hibernate.engine.spi.EntityKey;
4042
import org.hibernate.engine.spi.ExceptionConverter;
43+
import org.hibernate.engine.spi.LoadQueryInfluencers;
4144
import org.hibernate.engine.spi.SessionEventListenerManager;
4245
import org.hibernate.engine.spi.SessionFactoryImplementor;
4346
import org.hibernate.engine.spi.SharedSessionContractImplementor;
@@ -224,6 +227,24 @@ private static boolean isTransactionCoordinatorShared(SessionCreationOptions opt
224227
&& ( (SharedSessionCreationOptions) options ).isTransactionCoordinatorShared();
225228
}
226229

230+
protected final void setUpMultitenancy(SessionFactoryImplementor factory, LoadQueryInfluencers loadQueryInfluencers) {
231+
if ( factory.getDefinedFilterNames().contains( TenantIdBinder.FILTER_NAME ) ) {
232+
final Object tenantIdentifier = getTenantIdentifierValue();
233+
if ( tenantIdentifier == null ) {
234+
throw new HibernateException( "SessionFactory configured for multi-tenancy, but no tenant identifier specified" );
235+
}
236+
else {
237+
final CurrentTenantIdentifierResolver<Object> resolver = factory.getCurrentTenantIdentifierResolver();
238+
if ( resolver==null || !resolver.isRoot( tenantIdentifier ) ) {
239+
// turn on the filter, unless this is the "root" tenant with access to all partitions
240+
loadQueryInfluencers
241+
.enableFilter( TenantIdBinder.FILTER_NAME )
242+
.setParameter( TenantIdBinder.PARAMETER_NAME, tenantIdentifier );
243+
}
244+
}
245+
}
246+
}
247+
227248
private void logInconsistentOptions(SharedSessionCreationOptions sharedOptions) {
228249
if ( sharedOptions.shouldAutoJoinTransactions() ) {
229250
log.debug(

hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,7 @@
4949
import org.hibernate.TypeMismatchException;
5050
import org.hibernate.UnknownProfileException;
5151
import org.hibernate.UnresolvableObjectException;
52-
import org.hibernate.binder.internal.TenantIdBinder;
5352
import org.hibernate.collection.spi.PersistentCollection;
54-
import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
5553
import org.hibernate.engine.internal.StatefulPersistenceContext;
5654
import org.hibernate.engine.jdbc.LobCreator;
5755
import org.hibernate.engine.jdbc.NonContextualLobCreator;
@@ -268,7 +266,7 @@ public SessionImpl(SessionFactoryImpl factory, SessionCreationOptions options) {
268266
setHibernateFlushMode( getInitialFlushMode() );
269267
}
270268

271-
setUpMultitenancy( factory );
269+
setUpMultitenancy( factory, loadQueryInfluencers );
272270

273271
if ( log.isTraceEnabled() ) {
274272
log.tracef( "Opened Session [%s] at timestamp: %s", getSessionIdentifier(), currentTimeMillis() );
@@ -283,24 +281,6 @@ private FlushMode getInitialFlushMode() {
283281
: ConfigurationHelper.getFlushMode( getSessionProperty( HINT_FLUSH_MODE ), FlushMode.AUTO );
284282
}
285283

286-
private void setUpMultitenancy(SessionFactoryImplementor factory) {
287-
if ( factory.getDefinedFilterNames().contains( TenantIdBinder.FILTER_NAME ) ) {
288-
final Object tenantIdentifier = getTenantIdentifierValue();
289-
if ( tenantIdentifier == null ) {
290-
throw new HibernateException( "SessionFactory configured for multi-tenancy, but no tenant identifier specified" );
291-
}
292-
else {
293-
final CurrentTenantIdentifierResolver<Object> resolver = factory.getCurrentTenantIdentifierResolver();
294-
if ( resolver==null || !resolver.isRoot( tenantIdentifier ) ) {
295-
// turn on the filter, unless this is the "root" tenant with access to all partitions
296-
loadQueryInfluencers
297-
.enableFilter( TenantIdBinder.FILTER_NAME )
298-
.setParameter( TenantIdBinder.PARAMETER_NAME, tenantIdentifier );
299-
}
300-
}
301-
}
302-
}
303-
304284
protected StatefulPersistenceContext createPersistenceContext() {
305285
return new StatefulPersistenceContext( this );
306286
}

hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ public StatelessSessionImpl(SessionFactoryImpl factory, SessionCreationOptions o
7878
connectionProvided = options.getConnection() != null;
7979
temporaryPersistenceContext = new StatefulPersistenceContext( this );
8080
influencers = new LoadQueryInfluencers( getFactory() );
81+
setUpMultitenancy( factory, influencers );
8182
}
8283

8384
@Override

hibernate-core/src/test/java/org/hibernate/orm/test/tenantid/TenantIdTest.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import org.hibernate.HibernateError;
1010
import org.hibernate.PropertyValueException;
11+
import org.hibernate.StatelessSession;
1112
import org.hibernate.boot.SessionFactoryBuilder;
1213
import org.hibernate.boot.spi.MetadataImplementor;
1314
import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
@@ -20,6 +21,9 @@
2021
import org.hibernate.testing.orm.junit.SessionFactoryScope;
2122
import org.hibernate.testing.orm.junit.Setting;
2223
import org.hibernate.binder.internal.TenantIdBinder;
24+
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
25+
import org.hibernate.query.criteria.JpaCriteriaQuery;
26+
import org.hibernate.query.criteria.JpaRoot;
2327

2428
import org.hibernate.testing.orm.junit.SkipForDialect;
2529
import org.junit.jupiter.api.AfterEach;
@@ -28,6 +32,7 @@
2832
import jakarta.persistence.EntityManager;
2933
import jakarta.persistence.EntityManagerFactory;
3034

35+
import static org.assertj.core.api.Assertions.assertThat;
3136
import static org.hibernate.cfg.AvailableSettings.JAKARTA_HBM2DDL_DATABASE_ACTION;
3237
import static org.hibernate.internal.util.collections.CollectionHelper.toMap;
3338
import static org.hibernate.jpa.HibernateHints.HINT_TENANT_ID;
@@ -37,6 +42,8 @@
3742
import static org.junit.jupiter.api.Assertions.assertTrue;
3843
import static org.junit.jupiter.api.Assertions.fail;
3944

45+
import java.util.List;
46+
4047
@SessionFactory
4148
@DomainModel(annotatedClasses = { Account.class, Client.class, Record.class })
4249
@ServiceRegistry(
@@ -53,6 +60,7 @@ public void cleanup(SessionFactoryScope scope) {
5360
scope.inTransaction( session -> {
5461
session.createQuery("delete from Account").executeUpdate();
5562
session.createQuery("delete from Client").executeUpdate();
63+
session.createQuery("delete from Record").executeUpdate();
5664
});
5765
}
5866

@@ -217,6 +225,34 @@ public void testEntityManagerHint(SessionFactoryScope scope) {
217225
}
218226
}
219227

228+
229+
@Test
230+
public void tenantFilterWithStatelessSession(SessionFactoryScope scope) {
231+
currentTenant = "mine";
232+
Record myRecord1 = new Record();
233+
Record myRecord2 = new Record();
234+
235+
scope.inTransaction( session -> {
236+
session.persist(myRecord1);
237+
session.persist(myRecord2);
238+
} );
239+
scope.inStatelessTransaction( session -> {
240+
assertThat( listAllRecordsForTenant( session ) ).hasSize( 2 );
241+
} );
242+
243+
currentTenant = "yours";
244+
scope.inStatelessTransaction( session -> {
245+
assertThat( listAllRecordsForTenant( session ) ).isEmpty();
246+
} );
247+
}
248+
249+
private static List<Record> listAllRecordsForTenant(StatelessSession session) {
250+
HibernateCriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
251+
JpaCriteriaQuery<Record> criteriaQuery = criteriaBuilder.createQuery( Record.class );
252+
JpaRoot<Record> from = criteriaQuery.from( Record.class );
253+
return session.createQuery( criteriaQuery ).getResultList();
254+
}
255+
220256
private static void waitALittle() {
221257
try {
222258
Thread.sleep( 10 );

0 commit comments

Comments
 (0)