Skip to content

Commit 75d6031

Browse files
committed
HHH-19559 add hibernate.multi_tenant.set_schema
1 parent 0f256e4 commit 75d6031

13 files changed

+174
-12
lines changed

hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
187187
// multi-tenancy
188188
private boolean multiTenancyEnabled;
189189
private CurrentTenantIdentifierResolver<Object> currentTenantIdentifierResolver;
190+
private boolean setTenantSchemaEnabled;
190191

191192
// Queries
192193
private SqmFunctionRegistry sqmFunctionRegistry;
@@ -371,6 +372,7 @@ public SessionFactoryOptionsBuilder(StandardServiceRegistry serviceRegistry, Boo
371372
null
372373
);
373374
}
375+
setTenantSchemaEnabled = configurationService.getSetting( SET_TENANT_SCHEMA, BOOLEAN, false );
374376

375377
delayBatchFetchLoaderCreations =
376378
configurationService.getSetting( DELAY_ENTITY_LOADER_CREATIONS, BOOLEAN, true );
@@ -1003,6 +1005,11 @@ public boolean isMultiTenancyEnabled() {
10031005
return multiTenancyEnabled;
10041006
}
10051007

1008+
@Override
1009+
public boolean isSetTenantSchemaEnabled() {
1010+
return setTenantSchemaEnabled;
1011+
}
1012+
10061013
@Override
10071014
public CurrentTenantIdentifierResolver<Object> getCurrentTenantIdentifierResolver() {
10081015
return currentTenantIdentifierResolver;
@@ -1450,6 +1457,10 @@ public void applyCurrentTenantIdentifierResolver(CurrentTenantIdentifierResolver
14501457
this.currentTenantIdentifierResolver = (CurrentTenantIdentifierResolver<Object>) resolver;
14511458
}
14521459

1460+
public void applySetTenantSchema(boolean enabled) {
1461+
this.setTenantSchemaEnabled = enabled;
1462+
}
1463+
14531464
public void enableNamedQueryCheckingOnStartup(boolean enabled) {
14541465
this.namedQueryStartupCheckingEnabled = enabled;
14551466
}

hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,11 @@ public boolean isMultiTenancyEnabled() {
219219
return delegate.isMultiTenancyEnabled();
220220
}
221221

222+
@Override
223+
public boolean isSetTenantSchemaEnabled() {
224+
return delegate.isSetTenantSchemaEnabled();
225+
}
226+
222227
@Override
223228
public CurrentTenantIdentifierResolver<Object> getCurrentTenantIdentifierResolver() {
224229
return delegate.getCurrentTenantIdentifierResolver();

hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,17 @@ default SessionEventListener[] buildSessionEventListeners() {
304304
*/
305305
boolean isMultiTenancyEnabled();
306306

307+
/**
308+
* Should the schema be {@linkplain java.sql.Connection#setSchema set} to the
309+
* {@linkplain CurrentTenantIdentifierResolver#schemaName schema belonging to
310+
* the current tenant} each time a connection is obtained?
311+
*
312+
* @see org.hibernate.cfg.MultiTenancySettings#SET_TENANT_SCHEMA
313+
*
314+
* @since 7.1
315+
*/
316+
boolean isSetTenantSchemaEnabled();
317+
307318
/**
308319
* Obtain a reference to the
309320
* {@linkplain CurrentTenantIdentifierResolver current tenant identifier resolver},

hibernate-core/src/main/java/org/hibernate/cfg/MultiTenancySettings.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public interface MultiTenancySettings {
2626
* either:
2727
* <ul>
2828
* <li>an instance of {@code CurrentTenantIdentifierResolver},
29-
* <li>a {@link Class} representing an class that implements {@code CurrentTenantIdentifierResolver}, or
29+
* <li>a {@link Class} representing a class that implements {@code CurrentTenantIdentifierResolver}, or
3030
* <li>the name of a class that implements {@code CurrentTenantIdentifierResolver}.
3131
* </ul>
3232
*
@@ -42,4 +42,17 @@ public interface MultiTenancySettings {
4242
* This setting configures the name of the DataSource to use for this access
4343
*/
4444
String TENANT_IDENTIFIER_TO_USE_FOR_ANY_KEY = "hibernate.multi_tenant.datasource.identifier_for_any";
45+
46+
/**
47+
* Specifies that {@link java.sql.Connection#setSchema(String)}}
48+
* should be called with the current tenant id, or with the schema name
49+
* returned by {@link CurrentTenantIdentifierResolver#schemaName}.
50+
*
51+
* @settingDefault false
52+
*
53+
* @see CurrentTenantIdentifierResolver#schemaName
54+
*
55+
* @since 7.1
56+
*/
57+
String SET_TENANT_SCHEMA = "hibernate.multi_tenant.set_schema";
4558
}

hibernate-core/src/main/java/org/hibernate/context/spi/AbstractCurrentSessionContext.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,15 @@ public SessionFactoryImplementor factory() {
3434

3535
protected SessionBuilder baseSessionBuilder() {
3636
final SessionBuilderImplementor builder = factory.withOptions();
37-
final CurrentTenantIdentifierResolver<Object> resolver = factory.getCurrentTenantIdentifierResolver();
37+
final var resolver = factory.getCurrentTenantIdentifierResolver();
3838
if ( resolver != null ) {
3939
builder.tenantIdentifier( resolver.resolveCurrentTenantIdentifier() );
4040
}
4141
return builder;
4242
}
4343

4444
protected void validateExistingSession(Session existingSession) {
45-
final CurrentTenantIdentifierResolver<Object> resolver = factory.getCurrentTenantIdentifierResolver();
45+
final var resolver = factory.getCurrentTenantIdentifierResolver();
4646
if ( resolver != null && resolver.validateExistingCurrentSessions() ) {
4747
final Object currentValue = resolver.resolveCurrentTenantIdentifier();
4848
final JavaType<Object> tenantIdentifierJavaType = factory.getTenantIdentifierJavaType();

hibernate-core/src/main/java/org/hibernate/context/spi/CurrentTenantIdentifierResolver.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
*/
55
package org.hibernate.context.spi;
66

7+
import org.checkerframework.checker.nullness.qual.NonNull;
8+
79
/**
810
* A callback registered with the {@link org.hibernate.SessionFactory} that is
911
* responsible for resolving the current tenant identifier.
@@ -48,4 +50,20 @@ public interface CurrentTenantIdentifierResolver<T> {
4850
default boolean isRoot(T tenantId) {
4951
return false;
5052
}
53+
54+
/**
55+
* The name of the database schema for data belonging to the tenant with the
56+
* given identifier.
57+
* <p>
58+
* Called when {@value org.hibernate.cfg.MultiTenancySettings#SET_TENANT_SCHEMA}
59+
* is enabled.
60+
*
61+
* @param tenantIdentifier The tenant identifier
62+
* @return The name of the database schema belonging to that tenant
63+
*
64+
* @see org.hibernate.cfg.MultiTenancySettings#SET_TENANT_SCHEMA
65+
*/
66+
default @NonNull String schemaName(@NonNull T tenantIdentifier) {
67+
return tenantIdentifier.toString();
68+
}
5169
}

hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@
6767
import org.hibernate.stat.SessionStatistics;
6868
import org.hibernate.type.format.FormatMapper;
6969

70+
import java.sql.Connection;
71+
import java.sql.SQLException;
7072
import java.util.Collection;
7173
import java.util.List;
7274
import java.util.Map;
@@ -1247,4 +1249,14 @@ public SessionAssociationMarkers getSessionAssociationMarkers() {
12471249
public boolean isIdentifierRollbackEnabled() {
12481250
return delegate.isIdentifierRollbackEnabled();
12491251
}
1252+
1253+
@Override
1254+
public void afterObtainConnection(Connection connection) throws SQLException {
1255+
delegate.afterObtainConnection( connection );
1256+
}
1257+
1258+
@Override
1259+
public void beforeReleaseConnection(Connection connection) throws SQLException {
1260+
delegate.beforeReleaseConnection( connection );
1261+
}
12501262
}

hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionContractImplementor.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
*/
55
package org.hibernate.engine.spi;
66

7+
import java.sql.Connection;
8+
import java.sql.SQLException;
79
import java.util.Set;
810
import java.util.UUID;
911
import jakarta.persistence.TransactionRequiredException;
@@ -584,4 +586,31 @@ default boolean isStatelessSession() {
584586
*/
585587
@Incubating
586588
SessionAssociationMarkers getSessionAssociationMarkers();
589+
590+
/**
591+
* Called after a non-contextual JDBC connection is obtained.
592+
* <p>
593+
* Sets the schema to the schema belonging to the current tenant if:
594+
* <ol>
595+
* <li>{@value org.hibernate.cfg.MultiTenancySettings#SET_TENANT_SCHEMA} is enabled, and
596+
* <li>this session has an active tenant id.
597+
* </ol>
598+
*
599+
* @param connection A JDBC connection which was just acquired
600+
*
601+
* @since 7.1
602+
*/
603+
void afterObtainConnection(Connection connection) throws SQLException;
604+
605+
/**
606+
* Called right before non-contextual a JDBC connection is released.
607+
* <p>
608+
* Unset the schema which was set by {@link #afterObtainConnection},
609+
* if any.
610+
* .
611+
* @param connection The JDBC connection which is about to be released
612+
*
613+
* @since 7.1
614+
*/
615+
void beforeReleaseConnection(Connection connection) throws SQLException;
587616
}

hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionDelegatorBaseImpl.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
import org.hibernate.resource.transaction.spi.TransactionCoordinator;
4545
import org.hibernate.type.format.FormatMapper;
4646

47+
import java.sql.Connection;
48+
import java.sql.SQLException;
4749
import java.util.List;
4850
import java.util.Set;
4951
import java.util.TimeZone;
@@ -703,4 +705,14 @@ public SessionAssociationMarkers getSessionAssociationMarkers() {
703705
public boolean isIdentifierRollbackEnabled() {
704706
return delegate.isIdentifierRollbackEnabled();
705707
}
708+
709+
@Override
710+
public void afterObtainConnection(Connection connection) throws SQLException {
711+
delegate.afterObtainConnection( connection );
712+
}
713+
714+
@Override
715+
public void beforeReleaseConnection(Connection connection) throws SQLException {
716+
delegate.beforeReleaseConnection( connection );
717+
}
706718
}

hibernate-core/src/main/java/org/hibernate/generator/internal/TenantIdGeneration.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99

1010
import org.hibernate.PropertyValueException;
1111
import org.hibernate.annotations.TenantId;
12-
import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
1312
import org.hibernate.engine.spi.SessionFactoryImplementor;
1413
import org.hibernate.engine.spi.SharedSessionContractImplementor;
1514
import org.hibernate.generator.BeforeExecutionGenerator;
@@ -51,8 +50,7 @@ public Object generate(SharedSessionContractImplementor session, Object owner, O
5150
final SessionFactoryImplementor sessionFactory = session.getSessionFactory();
5251
final Object tenantId = session.getTenantIdentifierValue();
5352
if ( currentValue != null ) {
54-
final CurrentTenantIdentifierResolver<Object> resolver =
55-
sessionFactory.getCurrentTenantIdentifierResolver();
53+
final var resolver = sessionFactory.getCurrentTenantIdentifierResolver();
5654
if ( resolver != null && resolver.isRoot( tenantId ) ) {
5755
// the "root" tenant is allowed to set the tenant id explicitly
5856
return currentValue;

0 commit comments

Comments
 (0)