diff --git a/hibernate-core/src/main/java/org/hibernate/SessionBuilder.java b/hibernate-core/src/main/java/org/hibernate/SessionBuilder.java index 458450ad2a..28b726bb22 100644 --- a/hibernate-core/src/main/java/org/hibernate/SessionBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/SessionBuilder.java @@ -112,9 +112,21 @@ public interface SessionBuilder { * @param tenantIdentifier The tenant identifier. * * @return {@code this}, for method chaining + * @deprecated Use {@link #tenantIdentifier(Object)} instead */ + @Deprecated(forRemoval = true) SessionBuilder tenantIdentifier(String tenantIdentifier); + /** + * Define the tenant identifier to be associated with the opened session. + * + * @param tenantIdentifier The tenant identifier. + * + * @return {@code this}, for method chaining + * @since 6.4 + */ + SessionBuilder tenantIdentifier(Object tenantIdentifier); + /** * Add one or more {@link SessionEventListener} instances to the list of * listeners for the new session to be built. diff --git a/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java index b57451ce3a..b0e84e1c32 100644 --- a/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java @@ -31,6 +31,14 @@ public interface SharedSessionContract extends QueryProducer, Closeable, Seriali */ String getTenantIdentifier(); + /** + * Obtain the tenant identifier associated with this session. + * + * @return The tenant identifier associated with this session, or {@code null} + * @since 6.4 + */ + Object getTenantIdentifierValue(); + /** * End the session by releasing the JDBC connection and cleaning up. * diff --git a/hibernate-core/src/main/java/org/hibernate/StatelessSessionBuilder.java b/hibernate-core/src/main/java/org/hibernate/StatelessSessionBuilder.java index 74d8c00c80..b96a8af245 100644 --- a/hibernate-core/src/main/java/org/hibernate/StatelessSessionBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/StatelessSessionBuilder.java @@ -36,6 +36,18 @@ public interface StatelessSessionBuilder { * @param tenantIdentifier The tenant identifier. * * @return {@code this}, for method chaining + * @deprecated Use {@link #tenantIdentifier(Object)} instead */ + @Deprecated(forRemoval = true) StatelessSessionBuilder tenantIdentifier(String tenantIdentifier); + + /** + * Define the tenant identifier to be associated with the opened session. + * + * @param tenantIdentifier The tenant identifier. + * + * @return {@code this}, for method chaining + * @since 6.4 + */ + StatelessSessionBuilder tenantIdentifier(Object tenantIdentifier); } diff --git a/hibernate-core/src/main/java/org/hibernate/binder/internal/TenantIdBinder.java b/hibernate-core/src/main/java/org/hibernate/binder/internal/TenantIdBinder.java index 6aafdc60d7..9b02947928 100644 --- a/hibernate-core/src/main/java/org/hibernate/binder/internal/TenantIdBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/binder/internal/TenantIdBinder.java @@ -55,23 +55,7 @@ public class TenantIdBinder implements AttributeBinder { FILTER_NAME, "", singletonMap( PARAMETER_NAME, tenantIdType ) - ) { - // unfortunately the old APIs only accept String for a tenantId, so parse it - @Override - public Object processArgument(Object value) { - if (value==null) { - return null; - } - else if (value instanceof String) { - return getParameterJdbcMapping( PARAMETER_NAME ) - .getJavaTypeDescriptor() - .fromString((String) value); - } - else { - return value; - } - } - } + ) ); } else { @@ -89,27 +73,27 @@ public class TenantIdBinder implements AttributeBinder { } } persistentClass.addFilter( - FILTER_NAME, - columnNameOrFormula(property) - + " = :" - + PARAMETER_NAME, - true, - emptyMap(), - emptyMap() - ); + FILTER_NAME, + columnNameOrFormula( property ) + + " = :" + + PARAMETER_NAME, + true, + emptyMap(), + emptyMap() + ); - property.resetUpdateable(false); - property.resetOptional(false); + property.resetUpdateable( false ); + property.resetOptional( false ); } private String columnNameOrFormula(Property property) { - if ( property.getColumnSpan()!=1 ) { - throw new MappingException("@TenantId attribute must be mapped to a single column or formula"); + if ( property.getColumnSpan() != 1 ) { + throw new MappingException( "@TenantId attribute must be mapped to a single column or formula" ); } - Selectable selectable = property.getSelectables().get(0); + Selectable selectable = property.getSelectables().get( 0 ); return selectable.isFormula() - ? ((Formula) selectable).getFormula() - : ((Column) selectable).getName(); + ? ( (Formula) selectable ).getFormula() + : ( (Column) selectable ).getName(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/SessionFactoryBuilder.java b/hibernate-core/src/main/java/org/hibernate/boot/SessionFactoryBuilder.java index 3c02710122..49bd586b51 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/SessionFactoryBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/SessionFactoryBuilder.java @@ -377,7 +377,7 @@ public interface SessionFactoryBuilder { * * @see org.hibernate.cfg.AvailableSettings#MULTI_TENANT_IDENTIFIER_RESOLVER */ - SessionFactoryBuilder applyCurrentTenantIdentifierResolver(CurrentTenantIdentifierResolver resolver); + SessionFactoryBuilder applyCurrentTenantIdentifierResolver(CurrentTenantIdentifierResolver resolver); /** * If using the built-in JTA-based diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryBuilderImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryBuilderImpl.java index 5a257b75dc..97afb41a34 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryBuilderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryBuilderImpl.java @@ -262,7 +262,7 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement } @Override - public SessionFactoryBuilder applyCurrentTenantIdentifierResolver(CurrentTenantIdentifierResolver resolver) { + public SessionFactoryBuilder applyCurrentTenantIdentifierResolver(CurrentTenantIdentifierResolver resolver) { this.optionsBuilder.applyCurrentTenantIdentifierResolver( resolver ); return this; } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java index e414db7d8f..8b00e2fb8d 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java @@ -206,7 +206,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions { // multi-tenancy private boolean multiTenancyEnabled; - private CurrentTenantIdentifierResolver currentTenantIdentifierResolver; + private CurrentTenantIdentifierResolver currentTenantIdentifierResolver; // Queries private SqmFunctionRegistry sqmFunctionRegistry; @@ -1008,7 +1008,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions { } @Override - public CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver() { + public CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver() { return currentTenantIdentifierResolver; } @@ -1390,8 +1390,9 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions { this.multiTenancyEnabled = enabled; } - public void applyCurrentTenantIdentifierResolver(CurrentTenantIdentifierResolver resolver) { - this.currentTenantIdentifierResolver = resolver; + public void applyCurrentTenantIdentifierResolver(CurrentTenantIdentifierResolver resolver) { + //noinspection unchecked + this.currentTenantIdentifierResolver = (CurrentTenantIdentifierResolver) resolver; } public void enableNamedQueryCheckingOnStartup(boolean enabled) { diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryBuilder.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryBuilder.java index 1344aaa74b..794a950d11 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryBuilder.java @@ -210,7 +210,7 @@ public abstract class AbstractDelegatingSessionFactoryBuilder resolver) { delegate.applyCurrentTenantIdentifierResolver( resolver ); return getThis(); } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java index 5b33b5ac05..d93e07d02d 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java @@ -35,6 +35,7 @@ import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.query.sqm.sql.SqmTranslatorFactory; import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode; import org.hibernate.resource.jdbc.spi.StatementInspector; +import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.format.FormatMapper; /** @@ -213,10 +214,15 @@ public class AbstractDelegatingSessionFactoryOptions implements SessionFactoryOp } @Override - public CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver() { + public CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver() { return delegate.getCurrentTenantIdentifierResolver(); } + @Override + public JavaType getDefaultTenantIdentifierJavaType() { + return delegate.getDefaultTenantIdentifierJavaType(); + } + @Override public boolean isJtaTrackByThread() { return delegate.isJtaTrackByThread(); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java index e8ffc846bb..6825919b21 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java @@ -32,6 +32,8 @@ import org.hibernate.query.NullPrecedence; import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode; import org.hibernate.resource.jdbc.spi.StatementInspector; import org.hibernate.stat.Statistics; +import org.hibernate.type.descriptor.java.JavaType; +import org.hibernate.type.descriptor.java.ObjectJavaType; import org.hibernate.type.format.FormatMapper; /** @@ -154,7 +156,7 @@ public interface SessionFactoryOptions extends QueryEngineOptions { boolean isMultiTenancyEnabled(); - CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver(); + CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver(); boolean isJtaTrackByThread(); @@ -341,4 +343,14 @@ public interface SessionFactoryOptions extends QueryEngineOptions { */ @Incubating FormatMapper getXmlFormatMapper(); + + /** + * The default tenant identifier java type to use, in case no explicit tenant identifier property is defined. + * + * @since 6.4 + */ + @Incubating + default JavaType getDefaultTenantIdentifierJavaType() { + return ObjectJavaType.INSTANCE; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/cache/internal/DefaultCacheKeysFactory.java b/hibernate-core/src/main/java/org/hibernate/cache/internal/DefaultCacheKeysFactory.java index dd1c366b18..c32f1e6835 100644 --- a/hibernate-core/src/main/java/org/hibernate/cache/internal/DefaultCacheKeysFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/cache/internal/DefaultCacheKeysFactory.java @@ -73,12 +73,11 @@ public class DefaultCacheKeysFactory implements CacheKeysFactory { Object naturalIdValues, EntityPersister persister, SharedSessionContractImplementor session) { - NaturalIdCacheKey.NaturalIdCacheKeyBuilder builder = new NaturalIdCacheKey.NaturalIdCacheKeyBuilder( + return NaturalIdCacheKey.from( naturalIdValues, persister, session ); - return builder.build(); } public static Object staticGetEntityId(Object cacheKey) { diff --git a/hibernate-core/src/main/java/org/hibernate/cache/internal/NaturalIdCacheKey.java b/hibernate-core/src/main/java/org/hibernate/cache/internal/NaturalIdCacheKey.java index 64da34cc4e..f9823c35f9 100644 --- a/hibernate-core/src/main/java/org/hibernate/cache/internal/NaturalIdCacheKey.java +++ b/hibernate-core/src/main/java/org/hibernate/cache/internal/NaturalIdCacheKey.java @@ -6,18 +6,14 @@ */ package org.hibernate.cache.internal; -import java.io.IOException; -import java.io.ObjectInputStream; import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; import java.util.Objects; -import org.hibernate.Internal; import org.hibernate.cache.MutableCacheKeyBuilder; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.internal.util.ValueHolder; +import org.hibernate.metamodel.mapping.NaturalIdMapping; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.type.descriptor.java.JavaType; /** * Defines a key for caching natural identifier resolutions into the second level cache. @@ -33,88 +29,38 @@ public class NaturalIdCacheKey implements Serializable { private final String entityName; private final String tenantId; private final int hashCode; - // "transient" is important here -- NaturalIdCacheKey needs to be Serializable - private transient ValueHolder toString; - public static class NaturalIdCacheKeyBuilder implements MutableCacheKeyBuilder { - - private final String entityName; - private final String tenantIdentifier; - - private final List values; - private int hashCode; - - public NaturalIdCacheKeyBuilder( - Object naturalIdValues, - EntityPersister persister, - String entityName, - SharedSessionContractImplementor session) { - this.entityName = entityName; - this.tenantIdentifier = session.getTenantIdentifier(); - values = new ArrayList<>(); - persister.getNaturalIdMapping().addToCacheKey( this, naturalIdValues, session ); - } - - public NaturalIdCacheKeyBuilder( - Object naturalIdValues, - EntityPersister persister, - SharedSessionContractImplementor session) { - this( naturalIdValues, persister, persister.getRootEntityName(), session ); - } - - @Override - public void addValue(Object value) { - values.add( value ); - } - - @Override - public void addHashCode(int hashCode) { - this.hashCode = 37 * this.hashCode + hashCode; - } - - @Override - public NaturalIdCacheKey build() { - return new NaturalIdCacheKey( - values.toArray( new Object[0] ), - entityName, - tenantIdentifier, - hashCode - ); - } - } - - @Internal - public NaturalIdCacheKey(Object naturalIdValues, String entityName, String tenantId, int hashCode) { + private NaturalIdCacheKey(Object naturalIdValues, String entityName, String tenantId, int hashCode) { this.naturalIdValues = naturalIdValues; this.entityName = entityName; this.tenantId = tenantId; this.hashCode = hashCode; - - initTransients(); } - private void initTransients() { - this.toString = new ValueHolder<>( - () -> { - //Complex toString is needed as naturalIds for entities are not simply based on a single value like primary keys - //the only same way to differentiate the keys is to include the disassembled values in the string. - final StringBuilder toStringBuilder = new StringBuilder().append( entityName ).append( "##NaturalId[" ); - if ( naturalIdValues instanceof Object[] ) { - final Object[] values = (Object[]) naturalIdValues; - for ( int i = 0; i < values.length; i++ ) { - toStringBuilder.append( values[ i ] ); - if ( i + 1 < values.length ) { - toStringBuilder.append( ", " ); - } - } - } - else { - toStringBuilder.append( naturalIdValues ); - } - - return toStringBuilder.toString(); - } + public static NaturalIdCacheKey from( + Object naturalIdValues, + EntityPersister persister, + String entityName, + SharedSessionContractImplementor session) { + final NaturalIdMapping naturalIdMapping = persister.getNaturalIdMapping(); + final NaturalIdCacheKeyBuilder builder = new NaturalIdCacheKeyBuilder( + entityName, + session.getTenantIdentifier(), + naturalIdMapping.getJdbcTypeCount() ); + final JavaType tenantIdentifierJavaType = session.getFactory().getTenantIdentifierJavaType(); + final Object tenantId = session.getTenantIdentifierValue(); + // Add the tenant id to the hash code + builder.addHashCode( tenantId == null ? 0 : tenantIdentifierJavaType.extractHashCode( tenantId ) ); + naturalIdMapping.addToCacheKey( builder, naturalIdValues, session ); + return builder.build(); + } + + public static NaturalIdCacheKey from( + Object naturalIdValues, + EntityPersister persister, + SharedSessionContractImplementor session) { + return from( naturalIdValues, persister, persister.getRootEntityName(), session ); } @SuppressWarnings( {"UnusedDeclaration"}) @@ -132,11 +78,6 @@ public class NaturalIdCacheKey implements Serializable { return naturalIdValues; } - @Override - public String toString() { - return toString.getValue(); - } - @Override public int hashCode() { return this.hashCode; @@ -162,9 +103,59 @@ public class NaturalIdCacheKey implements Serializable { && Objects.deepEquals( this.naturalIdValues, other.naturalIdValues ); } - private void readObject(ObjectInputStream ois) - throws ClassNotFoundException, IOException { - ois.defaultReadObject(); - initTransients(); + @Override + public String toString() { + //Complex toString is needed as naturalIds for entities are not simply based on a single value like primary keys + //the only same way to differentiate the keys is to include the disassembled values in the string. + final StringBuilder toStringBuilder = new StringBuilder().append( entityName ).append( "##NaturalId[" ); + if ( naturalIdValues instanceof Object[] ) { + final Object[] values = (Object[]) naturalIdValues; + for ( int i = 0; i < values.length; i++ ) { + toStringBuilder.append( values[ i ] ); + if ( i + 1 < values.length ) { + toStringBuilder.append( ", " ); + } + } + } + else { + toStringBuilder.append( naturalIdValues ); + } + + return toStringBuilder.toString(); + } + + private static class NaturalIdCacheKeyBuilder implements MutableCacheKeyBuilder { + + private final String entityName; + private final String tenantIdentifier; + private final Object[] naturalIdValues; + private int hashCode; + private int naturalIdValueIndex; + + public NaturalIdCacheKeyBuilder(String entityName, String tenantIdentifier, int naturalIdValueCount) { + this.entityName = entityName; + this.tenantIdentifier = tenantIdentifier; + this.naturalIdValues = new Object[naturalIdValueCount]; + } + + @Override + public void addValue(Object value) { + naturalIdValues[naturalIdValueIndex++] = value; + } + + @Override + public void addHashCode(int hashCode) { + this.hashCode = 37 * this.hashCode + hashCode; + } + + @Override + public NaturalIdCacheKey build() { + return new NaturalIdCacheKey( + naturalIdValues, + entityName, + tenantIdentifier, + hashCode + ); + } } } diff --git a/hibernate-core/src/main/java/org/hibernate/cache/internal/SimpleCacheKeysFactory.java b/hibernate-core/src/main/java/org/hibernate/cache/internal/SimpleCacheKeysFactory.java index 5f8813f58b..6173f8a09d 100644 --- a/hibernate-core/src/main/java/org/hibernate/cache/internal/SimpleCacheKeysFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/cache/internal/SimpleCacheKeysFactory.java @@ -37,13 +37,12 @@ public class SimpleCacheKeysFactory implements CacheKeysFactory { EntityPersister persister, SharedSessionContractImplementor session) { // natural ids always need to be wrapped - NaturalIdCacheKey.NaturalIdCacheKeyBuilder builder = new NaturalIdCacheKey.NaturalIdCacheKeyBuilder( + return NaturalIdCacheKey.from( naturalIdValues, persister, null, session ); - return builder.build(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/cache/spi/QueryKey.java b/hibernate-core/src/main/java/org/hibernate/cache/spi/QueryKey.java index f1704fe139..ba0430cc4e 100644 --- a/hibernate-core/src/main/java/org/hibernate/cache/spi/QueryKey.java +++ b/hibernate-core/src/main/java/org/hibernate/cache/spi/QueryKey.java @@ -24,8 +24,7 @@ import org.hibernate.query.spi.QueryParameterBindings; * Note that the fields of this object must contain every explicit and * implicit setting and parameter argument that affects the result list * of the query, including things like the {@link #maxRows limit} and - * {@link #firstRow offset}, {@link #tenantIdentifier current tenant id}, - * and {@link #enabledFilterNames enabled filters}. + * {@link #firstRow offset} and {@link #enabledFilterNames enabled filters}. * * @author Gavin King * @author Steve Ebersole @@ -41,7 +40,7 @@ public class QueryKey implements Serializable { String sqlQueryString, Limit limit, QueryParameterBindings parameterBindings, - SharedSessionContractImplementor persistenceContext) { + SharedSessionContractImplementor session) { // todo (6.0) : here is where we should centralize cacheable-or-not // if this method returns null, the query should be considered un-cacheable // @@ -52,11 +51,10 @@ public class QueryKey implements Serializable { return new QueryKey( sqlQueryString, - parameterBindings.generateQueryKeyMemento( persistenceContext ), + parameterBindings.generateQueryKeyMemento( session ), limitToUse.getFirstRow(), limitToUse.getMaxRows(), - persistenceContext.getTenantIdentifier(), - persistenceContext.getLoadQueryInfluencers().getEnabledFilterNames() + session.getLoadQueryInfluencers().getEnabledFilterNames() ); } @@ -65,7 +63,6 @@ public class QueryKey implements Serializable { private final ParameterBindingsMemento parameterBindingsMemento; private final Integer firstRow; private final Integer maxRows; - private final String tenantIdentifier; private final String[] enabledFilterNames; /** @@ -79,13 +76,11 @@ public class QueryKey implements Serializable { ParameterBindingsMemento parameterBindingsMemento, Integer firstRow, Integer maxRows, - String tenantIdentifier, Set enabledFilterNames) { this.sqlQueryString = sql; this.parameterBindingsMemento = parameterBindingsMemento; this.firstRow = firstRow; this.maxRows = maxRows; - this.tenantIdentifier = tenantIdentifier; this.enabledFilterNames = enabledFilterNames.toArray( String[]::new ); this.hashCode = generateHashCode(); } @@ -109,7 +104,6 @@ public class QueryKey implements Serializable { // Don't include the firstRow and maxRows in the hash as these values are rarely useful for query caching // result = 37 * result + ( firstRow==null ? 0 : firstRow ); // result = 37 * result + ( maxRows==null ? 0 : maxRows ); - result = 37 * result + ( tenantIdentifier==null ? 0 : tenantIdentifier.hashCode() ); result = 37 * result + parameterBindingsMemento.hashCode(); result = 37 * result + Arrays.hashCode( enabledFilterNames ); return result; @@ -133,10 +127,6 @@ public class QueryKey implements Serializable { return false; } - if ( ! Objects.equals( tenantIdentifier, that.tenantIdentifier ) ) { - return false; - } - if ( ! Objects.equals( firstRow, that.firstRow ) || ! Objects.equals( maxRows, that.maxRows ) ) { return false; diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java b/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java index a37470fd2a..e2cf7be931 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java @@ -155,7 +155,7 @@ public class Configuration { private Interceptor interceptor; private SessionFactoryObserver sessionFactoryObserver; private StatementInspector statementInspector; - private CurrentTenantIdentifierResolver currentTenantIdentifierResolver; + private CurrentTenantIdentifierResolver currentTenantIdentifierResolver; private CustomEntityDirtinessStrategy customEntityDirtinessStrategy; private ColumnOrderingStrategy columnOrderingStrategy; private Properties properties; @@ -792,7 +792,7 @@ public class Configuration { /** * The {@link CurrentTenantIdentifierResolver}, if any, that was added to this configuration. */ - public CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver() { + public CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver() { return currentTenantIdentifierResolver; } @@ -801,7 +801,7 @@ public class Configuration { * * @return {@code this} for method chaining */ - public Configuration setCurrentTenantIdentifierResolver(CurrentTenantIdentifierResolver currentTenantIdentifierResolver) { + public Configuration setCurrentTenantIdentifierResolver(CurrentTenantIdentifierResolver currentTenantIdentifierResolver) { this.currentTenantIdentifierResolver = currentTenantIdentifierResolver; return this; } diff --git a/hibernate-core/src/main/java/org/hibernate/context/spi/AbstractCurrentSessionContext.java b/hibernate-core/src/main/java/org/hibernate/context/spi/AbstractCurrentSessionContext.java index 0056c76963..bdef2bdbac 100644 --- a/hibernate-core/src/main/java/org/hibernate/context/spi/AbstractCurrentSessionContext.java +++ b/hibernate-core/src/main/java/org/hibernate/context/spi/AbstractCurrentSessionContext.java @@ -6,13 +6,12 @@ */ package org.hibernate.context.spi; -import java.util.Objects; - import org.hibernate.Session; import org.hibernate.SessionBuilder; import org.hibernate.context.TenantIdentifierMismatchException; import org.hibernate.engine.spi.SessionBuilderImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.type.descriptor.java.JavaType; /** * Base support for {@link CurrentSessionContext} implementors. @@ -37,7 +36,7 @@ public abstract class AbstractCurrentSessionContext implements CurrentSessionCon protected SessionBuilder baseSessionBuilder() { final SessionBuilderImplementor builder = factory.withOptions(); - final CurrentTenantIdentifierResolver resolver = factory.getCurrentTenantIdentifierResolver(); + final CurrentTenantIdentifierResolver resolver = factory.getCurrentTenantIdentifierResolver(); if ( resolver != null ) { builder.tenantIdentifier( resolver.resolveCurrentTenantIdentifier() ); } @@ -45,16 +44,17 @@ public abstract class AbstractCurrentSessionContext implements CurrentSessionCon } protected void validateExistingSession(Session existingSession) { - final CurrentTenantIdentifierResolver resolver = factory.getCurrentTenantIdentifierResolver(); + final CurrentTenantIdentifierResolver resolver = factory.getCurrentTenantIdentifierResolver(); if ( resolver != null && resolver.validateExistingCurrentSessions() ) { - final String current = resolver.resolveCurrentTenantIdentifier(); - if ( !Objects.equals( existingSession.getTenantIdentifier(), current ) ) { + final Object currentValue = resolver.resolveCurrentTenantIdentifier(); + final JavaType tenantIdentifierJavaType = factory.getTenantIdentifierJavaType(); + if ( !tenantIdentifierJavaType.areEqual( currentValue, existingSession.getTenantIdentifierValue() ) ) { throw new TenantIdentifierMismatchException( String.format( "Reported current tenant identifier [%s] did not match tenant identifier from " + "existing session [%s]", - current, - existingSession.getTenantIdentifier() + tenantIdentifierJavaType.toString( currentValue ), + tenantIdentifierJavaType.toString( existingSession.getTenantIdentifierValue() ) ) ); } diff --git a/hibernate-core/src/main/java/org/hibernate/context/spi/CurrentTenantIdentifierResolver.java b/hibernate-core/src/main/java/org/hibernate/context/spi/CurrentTenantIdentifierResolver.java index 1dd37e6b5d..fdfef9720c 100644 --- a/hibernate-core/src/main/java/org/hibernate/context/spi/CurrentTenantIdentifierResolver.java +++ b/hibernate-core/src/main/java/org/hibernate/context/spi/CurrentTenantIdentifierResolver.java @@ -20,13 +20,13 @@ package org.hibernate.context.spi; * * @author Steve Ebersole */ -public interface CurrentTenantIdentifierResolver { +public interface CurrentTenantIdentifierResolver { /** * Resolve the current tenant identifier. - * + * * @return The current tenant identifier */ - String resolveCurrentTenantIdentifier(); + T resolveCurrentTenantIdentifier(); /** * Should we validate that the tenant identifier of a "current sessions" that @@ -47,7 +47,7 @@ public interface CurrentTenantIdentifierResolver { * * @return true is this is root tenant */ - default boolean isRoot(String tenantId) { + default boolean isRoot(T tenantId) { return false; } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/MultiTenantConnectionProviderInitiator.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/MultiTenantConnectionProviderInitiator.java index 5e41ca3366..1d0a2c8d40 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/MultiTenantConnectionProviderInitiator.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/MultiTenantConnectionProviderInitiator.java @@ -24,7 +24,7 @@ import org.jboss.logging.Logger; * * @author Steve Ebersole */ -public class MultiTenantConnectionProviderInitiator implements StandardServiceInitiator { +public class MultiTenantConnectionProviderInitiator implements StandardServiceInitiator> { private static final Logger log = Logger.getLogger( MultiTenantConnectionProviderInitiator.class ); /** @@ -33,12 +33,13 @@ public class MultiTenantConnectionProviderInitiator implements StandardServiceIn public static final MultiTenantConnectionProviderInitiator INSTANCE = new MultiTenantConnectionProviderInitiator(); @Override - public Class getServiceInitiated() { - return MultiTenantConnectionProvider.class; + public Class> getServiceInitiated() { + //noinspection unchecked + return (Class>) (Class) MultiTenantConnectionProvider.class; } @Override - public MultiTenantConnectionProvider initiateService(Map configurationValues, ServiceRegistryImplementor registry) { + public MultiTenantConnectionProvider initiateService(Map configurationValues, ServiceRegistryImplementor registry) { if ( !configurationValues.containsKey( AvailableSettings.MULTI_TENANT_CONNECTION_PROVIDER ) ) { // nothing to do, but given the separate hierarchies have to handle this here. return null; @@ -50,20 +51,20 @@ public class MultiTenantConnectionProviderInitiator implements StandardServiceIn // DataSourceBasedMultiTenantConnectionProviderImpl final Object dataSourceConfigValue = configurationValues.get( AvailableSettings.DATASOURCE ); if ( dataSourceConfigValue instanceof String ) { - return new DataSourceBasedMultiTenantConnectionProviderImpl(); + return new DataSourceBasedMultiTenantConnectionProviderImpl<>(); } return null; } - if ( configValue instanceof MultiTenantConnectionProvider ) { - return (MultiTenantConnectionProvider) configValue; + if ( configValue instanceof MultiTenantConnectionProvider ) { + return (MultiTenantConnectionProvider) configValue; } else { - final Class implClass; + final Class> implClass; if ( configValue instanceof Class ) { @SuppressWarnings("unchecked") - Class clazz = (Class) configValue; + Class> clazz = (Class>) configValue; implClass = clazz; } else { diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/AbstractDataSourceBasedMultiTenantConnectionProviderImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/AbstractDataSourceBasedMultiTenantConnectionProviderImpl.java index 842923e44a..69afaf5deb 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/AbstractDataSourceBasedMultiTenantConnectionProviderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/AbstractDataSourceBasedMultiTenantConnectionProviderImpl.java @@ -16,9 +16,9 @@ import org.hibernate.service.UnknownUnwrapTypeException; * Basic support for implementations of {@link MultiTenantConnectionProvider} based on DataSources. * @author Steve Ebersole */ -public abstract class AbstractDataSourceBasedMultiTenantConnectionProviderImpl implements MultiTenantConnectionProvider { +public abstract class AbstractDataSourceBasedMultiTenantConnectionProviderImpl implements MultiTenantConnectionProvider { protected abstract DataSource selectAnyDataSource(); - protected abstract DataSource selectDataSource(String tenantIdentifier); + protected abstract DataSource selectDataSource(T tenantIdentifier); @Override public Connection getAnyConnection() throws SQLException { @@ -31,12 +31,12 @@ public abstract class AbstractDataSourceBasedMultiTenantConnectionProviderImpl i } @Override - public Connection getConnection(String tenantIdentifier) throws SQLException { + public Connection getConnection(T tenantIdentifier) throws SQLException { return selectDataSource( tenantIdentifier ).getConnection(); } @Override - public void releaseConnection(String tenantIdentifier, Connection connection) throws SQLException { + public void releaseConnection(T tenantIdentifier, Connection connection) throws SQLException { connection.close(); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/AbstractMultiTenantConnectionProvider.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/AbstractMultiTenantConnectionProvider.java index b383666864..9e1a074e5c 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/AbstractMultiTenantConnectionProvider.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/AbstractMultiTenantConnectionProvider.java @@ -20,9 +20,9 @@ import org.hibernate.service.UnknownUnwrapTypeException; * * @author Steve Ebersole */ -public abstract class AbstractMultiTenantConnectionProvider implements MultiTenantConnectionProvider { +public abstract class AbstractMultiTenantConnectionProvider implements MultiTenantConnectionProvider { protected abstract ConnectionProvider getAnyConnectionProvider(); - protected abstract ConnectionProvider selectConnectionProvider(String tenantIdentifier); + protected abstract ConnectionProvider selectConnectionProvider(T tenantIdentifier); @Override public Connection getAnyConnection() throws SQLException { @@ -35,12 +35,12 @@ public abstract class AbstractMultiTenantConnectionProvider implements MultiTena } @Override - public Connection getConnection(String tenantIdentifier) throws SQLException { + public Connection getConnection(T tenantIdentifier) throws SQLException { return selectConnectionProvider( tenantIdentifier ).getConnection(); } @Override - public void releaseConnection(String tenantIdentifier, Connection connection) throws SQLException { + public void releaseConnection(T tenantIdentifier, Connection connection) throws SQLException { selectConnectionProvider( tenantIdentifier ).closeConnection( connection ); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/DataSourceBasedMultiTenantConnectionProviderImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/DataSourceBasedMultiTenantConnectionProviderImpl.java index 98177c1fbb..1fba0c9c46 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/DataSourceBasedMultiTenantConnectionProviderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/DataSourceBasedMultiTenantConnectionProviderImpl.java @@ -38,13 +38,13 @@ import static org.hibernate.cfg.MultiTenancySettings.TENANT_IDENTIFIER_TO_USE_FO * * @author Steve Ebersole */ -public class DataSourceBasedMultiTenantConnectionProviderImpl - extends AbstractDataSourceBasedMultiTenantConnectionProviderImpl +public class DataSourceBasedMultiTenantConnectionProviderImpl + extends AbstractDataSourceBasedMultiTenantConnectionProviderImpl implements ServiceRegistryAwareService, Stoppable { - private Map dataSourceMap; + private Map dataSourceMap; private JndiService jndiService; - private String tenantIdentifierForAny; + private T tenantIdentifierForAny; private String baseJndiNamespace; @Override @@ -53,7 +53,7 @@ public class DataSourceBasedMultiTenantConnectionProviderImpl } @Override - protected DataSource selectDataSource(String tenantIdentifier) { + protected DataSource selectDataSource(T tenantIdentifier) { DataSource dataSource = dataSourceMap().get( tenantIdentifier ); if ( dataSource == null ) { dataSource = (DataSource) jndiService.locate( baseJndiNamespace + '/' + tenantIdentifier ); @@ -62,7 +62,7 @@ public class DataSourceBasedMultiTenantConnectionProviderImpl return dataSource; } - private Map dataSourceMap() { + private Map dataSourceMap() { if ( dataSourceMap == null ) { dataSourceMap = new ConcurrentHashMap<>(); } @@ -92,12 +92,12 @@ public class DataSourceBasedMultiTenantConnectionProviderImpl if ( namedObject instanceof DataSource ) { final int loc = jndiName.lastIndexOf( '/' ); this.baseJndiNamespace = jndiName.substring( 0, loc ); - this.tenantIdentifierForAny = jndiName.substring( loc + 1 ); + this.tenantIdentifierForAny = (T) jndiName.substring( loc + 1 ); dataSourceMap().put( tenantIdentifierForAny, (DataSource) namedObject ); } else if ( namedObject instanceof Context ) { this.baseJndiNamespace = jndiName; - this.tenantIdentifierForAny = (String) serviceRegistry.getService( ConfigurationService.class ) + this.tenantIdentifierForAny = (T) serviceRegistry.getService( ConfigurationService.class ) .getSettings() .get( TENANT_IDENTIFIER_TO_USE_FOR_ANY_KEY ); if ( tenantIdentifierForAny == null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/MultiTenantConnectionProvider.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/MultiTenantConnectionProvider.java index 9668124fd6..3305fde364 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/MultiTenantConnectionProvider.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/MultiTenantConnectionProvider.java @@ -22,12 +22,14 @@ import org.hibernate.service.spi.Wrapped; * An application usually implements its own custom {@code MultiTenantConnectionProvider} * by subclassing {@link AbstractMultiTenantConnectionProvider}. * + * @param The tenant identifier type + * * @author Steve Ebersole * * @see AbstractMultiTenantConnectionProvider * @see org.hibernate.cfg.AvailableSettings#MULTI_TENANT_CONNECTION_PROVIDER */ -public interface MultiTenantConnectionProvider extends Service, Wrapped { +public interface MultiTenantConnectionProvider extends Service, Wrapped { /** * Allows access to the database metadata of the underlying database(s) in situations * where we do not have a tenant id (like startup processing, for example). @@ -57,7 +59,7 @@ public interface MultiTenantConnectionProvider extends Service, Wrapped { * @throws SQLException Indicates a problem opening a connection * @throws org.hibernate.HibernateException Indicates a problem otherwise obtaining a connection. */ - Connection getConnection(String tenantIdentifier) throws SQLException; + Connection getConnection(T tenantIdentifier) throws SQLException; /** * Release a connection from Hibernate use. @@ -68,7 +70,7 @@ public interface MultiTenantConnectionProvider extends Service, Wrapped { * @throws SQLException Indicates a problem closing the connection * @throws org.hibernate.HibernateException Indicates a problem otherwise releasing a connection. */ - void releaseConnection(String tenantIdentifier, Connection connection) throws SQLException; + void releaseConnection(T tenantIdentifier, Connection connection) throws SQLException; /** * Does this connection provider support aggressive release of JDBC connections and later diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/JdbcEnvironmentInitiator.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/JdbcEnvironmentInitiator.java index cf8fc47bed..ba9b3a4b6a 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/JdbcEnvironmentInitiator.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/JdbcEnvironmentInitiator.java @@ -392,7 +392,7 @@ public class JdbcEnvironmentInitiator implements StandardServiceInitiator multiTenantConnectionProvider = registry.getService( MultiTenantConnectionProvider.class ); return new MultiTenantConnectionProviderJdbcConnectionAccess( multiTenantConnectionProvider ); } } @@ -403,7 +403,7 @@ public class JdbcEnvironmentInitiator implements StandardServiceInitiator multiTenantConnectionProvider = registry.getService( MultiTenantConnectionProvider.class ); return new MultiTenantConnectionProviderJdbcConnectionAccess( multiTenantConnectionProvider ); } } @@ -436,13 +436,13 @@ public class JdbcEnvironmentInitiator implements StandardServiceInitiator connectionProvider; - public MultiTenantConnectionProviderJdbcConnectionAccess(MultiTenantConnectionProvider connectionProvider) { + public MultiTenantConnectionProviderJdbcConnectionAccess(MultiTenantConnectionProvider connectionProvider) { this.connectionProvider = connectionProvider; } - public MultiTenantConnectionProvider getConnectionProvider() { + public MultiTenantConnectionProvider getConnectionProvider() { return connectionProvider; } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/AbstractDelegatingSessionBuilder.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/AbstractDelegatingSessionBuilder.java index fa8df90e8f..f82bbe4299 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/AbstractDelegatingSessionBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/AbstractDelegatingSessionBuilder.java @@ -87,6 +87,12 @@ public abstract class AbstractDelegatingSessionBuilder implements SessionBuilder return this; } + @Override + public SessionBuilder tenantIdentifier(Object tenantIdentifier) { + delegate.tenantIdentifier( tenantIdentifier ); + return this; + } + @Override public SessionBuilder eventListeners(SessionEventListener... listeners) { delegate.eventListeners( listeners ); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/AbstractDelegatingSharedSessionBuilder.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/AbstractDelegatingSharedSessionBuilder.java index d08232a6c3..70d5e5c589 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/AbstractDelegatingSharedSessionBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/AbstractDelegatingSharedSessionBuilder.java @@ -123,6 +123,12 @@ public abstract class AbstractDelegatingSharedSessionBuilder implements SharedSe return this; } + @Override + public SharedSessionBuilder tenantIdentifier(Object tenantIdentifier) { + delegate.tenantIdentifier( tenantIdentifier ); + return this; + } + @Override public SharedSessionBuilder eventListeners(SessionEventListener... listeners) { delegate.eventListeners( listeners ); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java index ee55fb4651..326fcc9ede 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java @@ -110,6 +110,11 @@ public class SessionDelegatorBaseImpl implements SessionImplementor { return delegate.getTenantIdentifier(); } + @Override + public Object getTenantIdentifierValue() { + return delegate.getTenantIdentifierValue(); + } + @Override public UUID getSessionIdentifier() { return delegate.getSessionIdentifier(); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryDelegatingImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryDelegatingImpl.java index 341679b371..70c4da5b4b 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryDelegatingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryDelegatingImpl.java @@ -49,6 +49,7 @@ import org.hibernate.stat.spi.StatisticsImplementor; import org.hibernate.generator.Generator; import org.hibernate.type.Type; import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.spi.TypeConfiguration; /** @@ -260,10 +261,15 @@ public class SessionFactoryDelegatingImpl implements SessionFactoryImplementor, } @Override - public CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver() { + public CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver() { return delegate.getCurrentTenantIdentifierResolver(); } + @Override + public JavaType getTenantIdentifierJavaType() { + return delegate.getTenantIdentifierJavaType(); + } + @Override public FastSessionServices getFastSessionServices() { return delegate.getFastSessionServices(); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryImplementor.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryImplementor.java index 19894af67f..97df4d142a 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryImplementor.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryImplementor.java @@ -36,6 +36,7 @@ import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.stat.spi.StatisticsImplementor; import org.hibernate.generator.Generator; import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.spi.TypeConfiguration; /** @@ -143,7 +144,14 @@ public interface SessionFactoryImplementor CustomEntityDirtinessStrategy getCustomEntityDirtinessStrategy(); //todo make a Service ? - CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver(); + CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver(); + + /** + * The java type to use for a tenant identifier. + * + * @since 6.4 + */ + JavaType getTenantIdentifierJavaType(); /** * @return the FastSessionServices instance associated with this SessionFactory diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionLazyDelegator.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionLazyDelegator.java index b5b9a805c5..ab0245ccf5 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionLazyDelegator.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionLazyDelegator.java @@ -594,6 +594,11 @@ public class SessionLazyDelegator implements Session { return this.lazySession.get().getTenantIdentifier(); } + @Override + public Object getTenantIdentifierValue() { + return this.lazySession.get().getTenantIdentifierValue(); + } + @Override public void close() throws HibernateException { this.lazySession.get().close(); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionDelegatorBaseImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionDelegatorBaseImpl.java index 59e4c83cc3..ae2110094c 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionDelegatorBaseImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionDelegatorBaseImpl.java @@ -73,6 +73,11 @@ public class SharedSessionDelegatorBaseImpl implements SharedSessionContractImpl return delegate.getTenantIdentifier(); } + @Override + public Object getTenantIdentifierValue() { + return delegate.getTenantIdentifierValue(); + } + private QueryProducerImplementor queryDelegate() { return delegate; } diff --git a/hibernate-core/src/main/java/org/hibernate/event/jfr/internal/JfrEventManager.java b/hibernate-core/src/main/java/org/hibernate/event/jfr/internal/JfrEventManager.java index 4c4c0bc818..a11a7b98e6 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/jfr/internal/JfrEventManager.java +++ b/hibernate-core/src/main/java/org/hibernate/event/jfr/internal/JfrEventManager.java @@ -88,13 +88,15 @@ public class JfrEventManager { public static void completeJdbcConnectionAcquisitionEvent( JdbcConnectionAcquisitionEvent jdbcConnectionAcquisitionEvent, SharedSessionContractImplementor session, - String tenantId) { + Object tenantId) { if ( jdbcConnectionAcquisitionEvent.isEnabled() ) { jdbcConnectionAcquisitionEvent.end(); if ( jdbcConnectionAcquisitionEvent.shouldCommit() ) { jdbcConnectionAcquisitionEvent.executionTime = getExecutionTime( jdbcConnectionAcquisitionEvent.startedAt ); jdbcConnectionAcquisitionEvent.sessionIdentifier = getSessionIdentifier( session ); - jdbcConnectionAcquisitionEvent.tenantIdentifier = tenantId; + jdbcConnectionAcquisitionEvent.tenantIdentifier = tenantId == null ? null : session.getFactory() + .getTenantIdentifierJavaType() + .toString( tenantId ); jdbcConnectionAcquisitionEvent.commit(); } } @@ -112,13 +114,15 @@ public class JfrEventManager { public static void completeJdbcConnectionReleaseEvent( JdbcConnectionReleaseEvent jdbcConnectionReleaseEvent, SharedSessionContractImplementor session, - String tenantId) { + Object tenantId) { if ( jdbcConnectionReleaseEvent.isEnabled() ) { jdbcConnectionReleaseEvent.end(); if ( jdbcConnectionReleaseEvent.shouldCommit() ) { jdbcConnectionReleaseEvent.executionTime = getExecutionTime( jdbcConnectionReleaseEvent.startedAt ); jdbcConnectionReleaseEvent.sessionIdentifier = getSessionIdentifier( session ); - jdbcConnectionReleaseEvent.tenantIdentifier = tenantId; + jdbcConnectionReleaseEvent.tenantIdentifier = tenantId == null ? null : session.getFactory() + .getTenantIdentifierJavaType() + .toString( tenantId ); jdbcConnectionReleaseEvent.commit(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/generator/internal/TenantIdGeneration.java b/hibernate-core/src/main/java/org/hibernate/generator/internal/TenantIdGeneration.java index 9a7ce014f5..8b60131d92 100644 --- a/hibernate-core/src/main/java/org/hibernate/generator/internal/TenantIdGeneration.java +++ b/hibernate-core/src/main/java/org/hibernate/generator/internal/TenantIdGeneration.java @@ -6,23 +6,21 @@ */ package org.hibernate.generator.internal; -import org.hibernate.MappingException; +import java.lang.reflect.Member; +import java.util.EnumSet; + import org.hibernate.PropertyValueException; import org.hibernate.annotations.TenantId; import org.hibernate.context.spi.CurrentTenantIdentifierResolver; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.generator.BeforeExecutionGenerator; import org.hibernate.generator.EventType; import org.hibernate.generator.EventTypeSets; -import org.hibernate.generator.BeforeExecutionGenerator; import org.hibernate.generator.GeneratorCreationContext; import org.hibernate.type.descriptor.java.JavaType; -import java.lang.reflect.Member; -import java.util.EnumSet; - import static org.hibernate.generator.EventTypeSets.INSERT_ONLY; -import static org.hibernate.internal.util.ReflectHelper.getPropertyType; /** * A generator that produces the current tenant identifier @@ -34,14 +32,12 @@ public class TenantIdGeneration implements BeforeExecutionGenerator { private final String entityName; private final String propertyName; - private final Class propertyType; public TenantIdGeneration(TenantId annotation, Member member, GeneratorCreationContext context) { entityName = context.getPersistentClass() == null ? member.getDeclaringClass().getName() //it's an attribute of an embeddable : context.getPersistentClass().getEntityName(); propertyName = context.getProperty().getName(); - propertyType = getPropertyType( member ); } /** @@ -54,29 +50,27 @@ public class TenantIdGeneration implements BeforeExecutionGenerator { @Override public Object generate(SharedSessionContractImplementor session, Object owner, Object currentValue, EventType eventType) { - SessionFactoryImplementor sessionFactory = session.getSessionFactory(); - JavaType descriptor = sessionFactory.getTypeConfiguration().getJavaTypeRegistry() - .findDescriptor(propertyType); - if ( descriptor==null ) { - throw new MappingException( "unsupported tenant id property type: " + propertyType.getName() ); - } + final SessionFactoryImplementor sessionFactory = session.getSessionFactory(); + final JavaType tenantIdentifierJavaType = sessionFactory.getTenantIdentifierJavaType(); - String tenantId = session.getTenantIdentifier(); //unfortunately this is always a string in old APIs + final Object tenantId = session.getTenantIdentifierValue(); if ( currentValue != null ) { - CurrentTenantIdentifierResolver resolver = sessionFactory.getCurrentTenantIdentifierResolver(); - if ( resolver!=null && resolver.isRoot(tenantId) ) { + final CurrentTenantIdentifierResolver resolver = sessionFactory.getCurrentTenantIdentifierResolver(); + if ( resolver != null && resolver.isRoot( tenantId ) ) { // the "root" tenant is allowed to set the tenant id explicitly return currentValue; } - String currentTenantId = descriptor.toString(currentValue); - if ( !currentTenantId.equals(tenantId) ) { + if ( !tenantIdentifierJavaType.areEqual( currentValue, tenantId ) ) { throw new PropertyValueException( - "assigned tenant id differs from current tenant id: " - + currentTenantId + "!=" + tenantId, - entityName, propertyName + "assigned tenant id differs from current tenant id: " + + tenantIdentifierJavaType.toString( currentValue ) + + "!=" + + tenantIdentifierJavaType.toString( tenantId ), + entityName, + propertyName ); } } - return tenantId == null ? null : descriptor.fromString(tenantId); //convert to the model type + return tenantId; } } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java index d7d979f57d..0d73071b53 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java @@ -152,7 +152,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont private final Interceptor interceptor; - private final String tenantIdentifier; + private final Object tenantIdentifier; private final TimeZone jdbcTimeZone; // mutable state @@ -257,8 +257,8 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont ); } - private String getTenantId( SessionFactoryImpl factory, SessionCreationOptions options ) { - final String tenantIdentifier = options.getTenantIdentifier(); + private Object getTenantId( SessionFactoryImpl factory, SessionCreationOptions options ) { + final Object tenantIdentifier = options.getTenantIdentifierValue(); if ( factory.getSessionFactoryOptions().isMultiTenancyEnabled() && tenantIdentifier == null ) { throw new HibernateException( "SessionFactory configured for multi-tenancy, but no tenant identifier specified" ); } @@ -367,6 +367,14 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont @Override public String getTenantIdentifier() { + if ( tenantIdentifier == null ) { + return null; + } + return factory.getTenantIdentifierJavaType().toString( tenantIdentifier ); + } + + @Override + public Object getTenantIdentifierValue() { return tenantIdentifier; } @@ -611,7 +619,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont } else { jdbcConnectionAccess = new ContextualJdbcConnectionAccess( - getTenantIdentifier(), + getTenantIdentifierValue(), getEventListenerManager(), fastSessionServices.multiTenantConnectionProvider, this diff --git a/hibernate-core/src/main/java/org/hibernate/internal/ContextualJdbcConnectionAccess.java b/hibernate-core/src/main/java/org/hibernate/internal/ContextualJdbcConnectionAccess.java index 2fa07fb74b..eb235282aa 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/ContextualJdbcConnectionAccess.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/ContextualJdbcConnectionAccess.java @@ -23,16 +23,16 @@ import org.hibernate.event.jfr.JdbcConnectionReleaseEvent; * @author Steve Ebersole */ public class ContextualJdbcConnectionAccess implements JdbcConnectionAccess, Serializable { - private final String tenantIdentifier; + private final Object tenantIdentifier; private final SessionEventListener listener; - private final MultiTenantConnectionProvider connectionProvider; + private final MultiTenantConnectionProvider connectionProvider; private final SharedSessionContractImplementor session; public ContextualJdbcConnectionAccess( - String tenantIdentifier, + Object tenantIdentifier, SessionEventListener listener, - MultiTenantConnectionProvider connectionProvider, + MultiTenantConnectionProvider connectionProvider, SharedSessionContractImplementor session) { this.tenantIdentifier = tenantIdentifier; this.listener = listener; diff --git a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java index 57c1743db7..92606b7b15 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java @@ -158,7 +158,7 @@ public final class FastSessionServices { final TimeZoneStorageStrategy defaultTimeZoneStorageStrategy; final boolean requiresMultiTenantConnectionProvider; final ConnectionProvider connectionProvider; - final MultiTenantConnectionProvider multiTenantConnectionProvider; + final MultiTenantConnectionProvider multiTenantConnectionProvider; final ClassLoaderService classLoaderService; final TransactionCoordinatorBuilder transactionCoordinatorBuilder; public final JdbcServices jdbcServices; diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionCreationOptions.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionCreationOptions.java index ae1dcb8587..dffccc7edf 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionCreationOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionCreationOptions.java @@ -46,6 +46,8 @@ public interface SessionCreationOptions { String getTenantIdentifier(); + Object getTenantIdentifierValue(); + TimeZone getJdbcTimeZone(); /** diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java index 96689f1d94..69398b104e 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java @@ -37,6 +37,7 @@ import org.hibernate.SessionFactoryObserver; import org.hibernate.StatelessSession; import org.hibernate.StatelessSessionBuilder; import org.hibernate.UnknownFilterException; +import org.hibernate.binder.internal.TenantIdBinder; import org.hibernate.boot.cfgxml.spi.CfgXmlAccessService; import org.hibernate.boot.model.relational.SqlStringGenerationContext; import org.hibernate.boot.model.relational.internal.SqlStringGenerationContextImpl; @@ -78,6 +79,7 @@ import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.RootClass; import org.hibernate.metadata.CollectionMetadata; import org.hibernate.metamodel.internal.RuntimeMetamodelsImpl; +import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.model.domain.internal.MappingMetamodelImpl; import org.hibernate.metamodel.model.domain.spi.JpaMetamodelImplementor; import org.hibernate.metamodel.spi.MappingMetamodelImplementor; @@ -111,6 +113,7 @@ import org.hibernate.service.spi.SessionFactoryServiceRegistryFactory; import org.hibernate.stat.spi.StatisticsImplementor; import org.hibernate.type.Type; import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.spi.TypeConfiguration; import org.jboss.logging.Logger; @@ -185,6 +188,7 @@ public class SessionFactoryImpl extends QueryParameterBindingTypeResolverImpl im private final transient Map identifierGenerators; private final transient Map filters; private final transient Map fetchProfiles; + private final transient JavaType tenantIdentifierJavaType; private final transient FastSessionServices fastSessionServices; private final transient WrapperOptions wrapperOptions; @@ -240,6 +244,17 @@ public class SessionFactoryImpl extends QueryParameterBindingTypeResolverImpl im filters = new HashMap<>( bootMetamodel.getFilterDefinitions() ); LOG.debugf( "Session factory constructed with filter configurations : %s", filters ); + final FilterDefinition tenantFilter = filters.get( TenantIdBinder.FILTER_NAME ); + if ( tenantFilter == null ) { + tenantIdentifierJavaType = options.getDefaultTenantIdentifierJavaType(); + } + else { + final JdbcMapping jdbcMapping = tenantFilter.getParameterJdbcMapping( TenantIdBinder.PARAMETER_NAME ); + assert jdbcMapping != null; + //noinspection unchecked + tenantIdentifierJavaType = jdbcMapping.getJavaTypeDescriptor(); + } + entityNameResolver = new CoordinatingEntityNameResolver( this, getInterceptor() ); schemaManager = new SchemaManagerImpl( this, bootMetamodel ); @@ -502,7 +517,7 @@ public class SessionFactoryImpl extends QueryParameterBindingTypeResolverImpl im } private SessionBuilderImpl createDefaultSessionOpenOptionsIfPossible() { - final CurrentTenantIdentifierResolver currentTenantIdentifierResolver = getCurrentTenantIdentifierResolver(); + final CurrentTenantIdentifierResolver currentTenantIdentifierResolver = getCurrentTenantIdentifierResolver(); if ( currentTenantIdentifierResolver == null ) { return withOptions(); } @@ -707,7 +722,7 @@ public class SessionFactoryImpl extends QueryParameterBindingTypeResolverImpl im if ( map != null ) { //noinspection SuspiciousMethodCalls - final String tenantIdHint = (String) map.get( HINT_TENANT_ID ); + final Object tenantIdHint = map.get( HINT_TENANT_ID ); if ( tenantIdHint != null ) { builder = (SessionBuilderImplementor) builder.tenantIdentifier( tenantIdHint ); } @@ -1198,7 +1213,7 @@ public class SessionFactoryImpl extends QueryParameterBindingTypeResolverImpl im private FlushMode flushMode; private boolean autoClose; private boolean autoClear; - private String tenantIdentifier; + private Object tenantIdentifier; private TimeZone jdbcTimeZone; private boolean explicitNoInterceptor; private int defaultBatchFetchSize; @@ -1223,7 +1238,7 @@ public class SessionFactoryImpl extends QueryParameterBindingTypeResolverImpl im this.defaultBatchFetchSize = sessionFactoryOptions.getDefaultBatchFetchSize(); this.subselectFetchEnabled = sessionFactoryOptions.isSubselectFetchEnabled(); - final CurrentTenantIdentifierResolver currentTenantIdentifierResolver = sessionFactory.getCurrentTenantIdentifierResolver(); + final CurrentTenantIdentifierResolver currentTenantIdentifierResolver = sessionFactory.getCurrentTenantIdentifierResolver(); if ( currentTenantIdentifierResolver != null ) { tenantIdentifier = currentTenantIdentifierResolver.resolveCurrentTenantIdentifier(); } @@ -1293,6 +1308,14 @@ public class SessionFactoryImpl extends QueryParameterBindingTypeResolverImpl im @Override public String getTenantIdentifier() { + if ( tenantIdentifier == null ) { + return null; + } + return sessionFactory.getTenantIdentifierJavaType().toString( tenantIdentifier ); + } + + @Override + public Object getTenantIdentifierValue() { return tenantIdentifier; } @@ -1377,6 +1400,12 @@ public class SessionFactoryImpl extends QueryParameterBindingTypeResolverImpl im return this; } + @Override + public SessionBuilderImpl tenantIdentifier(Object tenantIdentifier) { + this.tenantIdentifier = tenantIdentifier; + return this; + } + @Override public SessionBuilderImpl eventListeners(SessionEventListener... listeners) { if ( this.listeners == null ) { @@ -1410,12 +1439,12 @@ public class SessionFactoryImpl extends QueryParameterBindingTypeResolverImpl im public static class StatelessSessionBuilderImpl implements StatelessSessionBuilder, SessionCreationOptions { private final SessionFactoryImpl sessionFactory; private Connection connection; - private String tenantIdentifier; + private Object tenantIdentifier; public StatelessSessionBuilderImpl(SessionFactoryImpl sessionFactory) { this.sessionFactory = sessionFactory; - CurrentTenantIdentifierResolver tenantIdentifierResolver = sessionFactory.getCurrentTenantIdentifierResolver(); + CurrentTenantIdentifierResolver tenantIdentifierResolver = sessionFactory.getCurrentTenantIdentifierResolver(); if ( tenantIdentifierResolver != null ) { tenantIdentifier = tenantIdentifierResolver.resolveCurrentTenantIdentifier(); } @@ -1438,6 +1467,12 @@ public class SessionFactoryImpl extends QueryParameterBindingTypeResolverImpl im return this; } + @Override + public StatelessSessionBuilder tenantIdentifier(Object tenantIdentifier) { + this.tenantIdentifier = tenantIdentifier; + return this; + } + @Override public boolean shouldAutoJoinTransactions() { return true; @@ -1491,6 +1526,14 @@ public class SessionFactoryImpl extends QueryParameterBindingTypeResolverImpl im @Override public String getTenantIdentifier() { + if ( tenantIdentifier == null ) { + return null; + } + return sessionFactory.getTenantIdentifierJavaType().toString( tenantIdentifier ); + } + + @Override + public Object getTenantIdentifierValue() { return tenantIdentifier; } @@ -1516,10 +1559,15 @@ public class SessionFactoryImpl extends QueryParameterBindingTypeResolverImpl im } @Override - public CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver() { + public CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver() { return getSessionFactoryOptions().getCurrentTenantIdentifierResolver(); } + @Override + public JavaType getTenantIdentifierJavaType() { + return tenantIdentifierJavaType; + } + // Serialization handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java index a9785b52e1..97a672118e 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -286,12 +286,12 @@ public class SessionImpl private void setUpMultitenancy(SessionFactoryImplementor factory) { if ( factory.getDefinedFilterNames().contains( TenantIdBinder.FILTER_NAME ) ) { - final String tenantIdentifier = getTenantIdentifier(); + final Object tenantIdentifier = getTenantIdentifierValue(); if ( tenantIdentifier == null ) { throw new HibernateException( "SessionFactory configured for multi-tenancy, but no tenant identifier specified" ); } else { - final CurrentTenantIdentifierResolver resolver = factory.getCurrentTenantIdentifierResolver(); + final CurrentTenantIdentifierResolver 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 @@ -2090,7 +2090,7 @@ public class SessionImpl private SharedSessionBuilderImpl(SessionImpl session) { super( (SessionFactoryImpl) session.getFactory() ); this.session = session; - super.tenantIdentifier( session.getTenantIdentifier() ); + super.tenantIdentifier( session.getTenantIdentifierValue() ); } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -2103,6 +2103,12 @@ public class SessionImpl throw new SessionException( "Cannot redefine tenant identifier on child session" ); } + @Override + public SharedSessionBuilderImpl tenantIdentifier(Object tenantIdentifier) { + // todo : is this always true? Or just in the case of sharing JDBC resources? + throw new SessionException( "Cannot redefine tenant identifier on child session" ); + } + @Override public SharedSessionBuilderImpl interceptor() { super.interceptor( session.getInterceptor() ); diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index aedae13850..8633a1d7b8 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -4146,8 +4146,7 @@ public abstract class AbstractEntityPersister // check to see if it is in the second-level cache if ( session.getCacheMode().isGetEnabled() && canReadFromCache() ) { final EntityDataAccess cache = getCacheAccessStrategy(); - final String tenantId = session.getTenantIdentifier(); - final Object ck = cache.generateCacheKey( id, this, session.getFactory(), tenantId ); + final Object ck = cache.generateCacheKey( id, this, session.getFactory(), session.getTenantIdentifier() ); final Object ce = CacheHelper.fromSharedCache( session, ck, this, getCacheAccessStrategy() ); if ( ce != null ) { return false; diff --git a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingsImpl.java b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingsImpl.java index f7ed33a7ae..112a88aa7f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingsImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingsImpl.java @@ -173,6 +173,10 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings { @Override public QueryKey.ParameterBindingsMemento generateQueryKeyMemento(SharedSessionContractImplementor session) { final MutableCacheKeyImpl mutableCacheKey = new MutableCacheKeyImpl( parameterBindingMap.size() ); + final JavaType tenantIdentifierJavaType = session.getFactory().getTenantIdentifierJavaType(); + final Object tenantId = session.getTenantIdentifierValue(); + mutableCacheKey.addValue( tenantIdentifierJavaType.getMutabilityPlan().disassemble( tenantId, session ) ); + mutableCacheKey.addHashCode( tenantId == null ? 0 : tenantIdentifierJavaType.extractHashCode( tenantId ) ); // We know that parameters are consumed in processing order, this ensures consistency of generated cache keys parameterMetadata.visitParameters( queryParameter -> { final QueryParameterBinding binding = parameterBindingMap.get( queryParameter ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/ConfigurableMultiTenantConnectionProvider.java b/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/ConfigurableMultiTenantConnectionProvider.java index 708b37a07f..282898ac75 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/ConfigurableMultiTenantConnectionProvider.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/ConfigurableMultiTenantConnectionProvider.java @@ -17,7 +17,7 @@ import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; */ //tag::multitenacy-hibernate-ConfigurableMultiTenantConnectionProvider-example[] public class ConfigurableMultiTenantConnectionProvider - extends AbstractMultiTenantConnectionProvider { + extends AbstractMultiTenantConnectionProvider { private final Map connectionProviderMap = new HashMap<>(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/TestingConnectionProvider.java b/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/TestingConnectionProvider.java index e6bdca8373..57f5a6db13 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/TestingConnectionProvider.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/TestingConnectionProvider.java @@ -15,7 +15,7 @@ import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; /** * @author Steve Ebersole */ -public class TestingConnectionProvider extends AbstractMultiTenantConnectionProvider { +public class TestingConnectionProvider extends AbstractMultiTenantConnectionProvider { private Map connectionProviderMap; public TestingConnectionProvider(Map connectionProviderMap) { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/discriminator/DiscriminatorMultiTenancyTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/discriminator/DiscriminatorMultiTenancyTest.java index 3a19097115..fc02c5ab72 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/discriminator/DiscriminatorMultiTenancyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/discriminator/DiscriminatorMultiTenancyTest.java @@ -16,10 +16,8 @@ import org.hibernate.Session; import org.hibernate.boot.Metadata; import org.hibernate.boot.MetadataSources; import org.hibernate.boot.SessionFactoryBuilder; -import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.cfg.Environment; import org.hibernate.context.spi.CurrentTenantIdentifierResolver; -import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.mapping.PersistentClass; @@ -184,7 +182,7 @@ public class DiscriminatorMultiTenancyTest extends BaseUnitTestCase { } ); } - private static class TestCurrentTenantIdentifierResolver implements CurrentTenantIdentifierResolver { + private static class TestCurrentTenantIdentifierResolver implements CurrentTenantIdentifierResolver { private String currentTenantIdentifier; private final AtomicBoolean postBoot = new AtomicBoolean(false); @@ -193,7 +191,7 @@ public class DiscriminatorMultiTenancyTest extends BaseUnitTestCase { } @Override - public String resolveCurrentTenantIdentifier() { + public Object resolveCurrentTenantIdentifier() { if ( postBoot.get() == false ) { //Check to prevent any optimisation which might want to cache the tenantId too early during bootstrap: //it's a common use case to want to provide the tenantId, for example, via a ThreadLocal. diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/schema/AbstractSchemaBasedMultiTenancyTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/schema/AbstractSchemaBasedMultiTenancyTest.java index 6d4d9a068c..5347c033f6 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/schema/AbstractSchemaBasedMultiTenancyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/schema/AbstractSchemaBasedMultiTenancyTest.java @@ -13,7 +13,6 @@ import org.hibernate.SessionBuilder; import org.hibernate.boot.Metadata; import org.hibernate.boot.MetadataSources; import org.hibernate.boot.SessionFactoryBuilder; -import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.cfg.Environment; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider; @@ -46,7 +45,7 @@ import static org.hibernate.testing.transaction.TransactionUtil.doInHibernateSes /** * @author Steve Ebersole */ -public abstract class AbstractSchemaBasedMultiTenancyTest extends BaseUnitTestCase { +public abstract class AbstractSchemaBasedMultiTenancyTest, C extends ConnectionProvider> extends BaseUnitTestCase { protected C acmeProvider; protected C jbossProvider; @@ -271,7 +270,7 @@ public abstract class AbstractSchemaBasedMultiTenancyTest { private String currentTenantIdentifier; @Override @@ -54,7 +54,7 @@ public class CurrentTenantResolverMultiTenancyTest extends SchemaBasedMultiTenan } @Override - public String resolveCurrentTenantIdentifier() { + public Object resolveCurrentTenantIdentifier() { return currentTenantIdentifier; } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/schema/SchemaBasedDataSourceMultiTenancyTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/schema/SchemaBasedDataSourceMultiTenancyTest.java index 33e07b6d4e..f8e711b42b 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/schema/SchemaBasedDataSourceMultiTenancyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/schema/SchemaBasedDataSourceMultiTenancyTest.java @@ -9,7 +9,6 @@ package org.hibernate.orm.test.multitenancy.schema; import javax.sql.DataSource; import org.hibernate.HibernateException; -import org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl; import org.hibernate.engine.jdbc.connections.spi.AbstractDataSourceBasedMultiTenantConnectionProviderImpl; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider; @@ -28,12 +27,12 @@ import static org.junit.Assert.assertThat; */ @RequiresDialectFeature( value = ConnectionProviderBuilder.class ) public class SchemaBasedDataSourceMultiTenancyTest extends AbstractSchemaBasedMultiTenancyTest< - AbstractDataSourceBasedMultiTenantConnectionProviderImpl, ConnectionProvider> { + AbstractDataSourceBasedMultiTenantConnectionProviderImpl, ConnectionProvider> { - protected AbstractDataSourceBasedMultiTenantConnectionProviderImpl buildMultiTenantConnectionProvider() { + protected AbstractDataSourceBasedMultiTenantConnectionProviderImpl buildMultiTenantConnectionProvider() { acmeProvider = ConnectionProviderBuilder.buildDataSourceConnectionProvider( "acme" ); jbossProvider = ConnectionProviderBuilder.buildDataSourceConnectionProvider( "jboss" ); - return new AbstractDataSourceBasedMultiTenantConnectionProviderImpl() { + return new AbstractDataSourceBasedMultiTenantConnectionProviderImpl<>() { @Override protected DataSource selectAnyDataSource() { return acmeProvider.unwrap( DataSource.class ); @@ -55,7 +54,7 @@ public class SchemaBasedDataSourceMultiTenancyTest extends AbstractSchemaBasedM @Test @TestForIssue( jiraKey = "HHH-11651") public void testUnwrappingConnectionProvider() { - final MultiTenantConnectionProvider multiTenantConnectionProvider = serviceRegistry.getService( + final MultiTenantConnectionProvider multiTenantConnectionProvider = serviceRegistry.getService( MultiTenantConnectionProvider.class ); final DataSource dataSource = multiTenantConnectionProvider.unwrap( DataSource.class ); assertThat( dataSource, is( notNullValue() ) ); @@ -64,7 +63,7 @@ public class SchemaBasedDataSourceMultiTenancyTest extends AbstractSchemaBasedM @Test @TestForIssue(jiraKey = "HHH-11651") public void testUnwrappingAbstractMultiTenantConnectionProvider() { - final MultiTenantConnectionProvider multiTenantConnectionProvider = serviceRegistry.getService( + final MultiTenantConnectionProvider multiTenantConnectionProvider = serviceRegistry.getService( MultiTenantConnectionProvider.class ); final AbstractDataSourceBasedMultiTenantConnectionProviderImpl dataSourceBasedMultiTenantConnectionProvider = multiTenantConnectionProvider.unwrap( AbstractDataSourceBasedMultiTenantConnectionProviderImpl.class ); @@ -74,9 +73,9 @@ public class SchemaBasedDataSourceMultiTenancyTest extends AbstractSchemaBasedM @Test @TestForIssue(jiraKey = "HHH-11651") public void testUnwrappingMultiTenantConnectionProvider() { - final MultiTenantConnectionProvider multiTenantConnectionProvider = serviceRegistry.getService( + final MultiTenantConnectionProvider multiTenantConnectionProvider = serviceRegistry.getService( MultiTenantConnectionProvider.class ); - final MultiTenantConnectionProvider connectionProvider = multiTenantConnectionProvider.unwrap( + final MultiTenantConnectionProvider connectionProvider = multiTenantConnectionProvider.unwrap( MultiTenantConnectionProvider.class ); assertThat( connectionProvider, is( notNullValue() ) ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/schema/SchemaBasedMultiTenancyTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/schema/SchemaBasedMultiTenancyTest.java index 70a5613288..edcb061fc0 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/schema/SchemaBasedMultiTenancyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/schema/SchemaBasedMultiTenancyTest.java @@ -7,7 +7,6 @@ package org.hibernate.orm.test.multitenancy.schema; import org.hibernate.HibernateException; -import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl; import org.hibernate.engine.jdbc.connections.spi.AbstractMultiTenantConnectionProvider; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider; @@ -26,12 +25,12 @@ import static org.junit.Assert.assertThat; */ @RequiresDialectFeature( value = ConnectionProviderBuilder.class ) public class SchemaBasedMultiTenancyTest extends AbstractSchemaBasedMultiTenancyTest< - AbstractMultiTenantConnectionProvider, ConnectionProvider> { + AbstractMultiTenantConnectionProvider, ConnectionProvider> { - protected AbstractMultiTenantConnectionProvider buildMultiTenantConnectionProvider() { + protected AbstractMultiTenantConnectionProvider buildMultiTenantConnectionProvider() { acmeProvider = ConnectionProviderBuilder.buildConnectionProvider( "acme" ); jbossProvider = ConnectionProviderBuilder.buildConnectionProvider( "jboss" ); - return new AbstractMultiTenantConnectionProvider() { + return new AbstractMultiTenantConnectionProvider<>() { @Override protected ConnectionProvider getAnyConnectionProvider() { return acmeProvider; @@ -53,7 +52,7 @@ public class SchemaBasedMultiTenancyTest extends AbstractSchemaBasedMultiTenancy @Test @TestForIssue( jiraKey = "HHH-11651") public void testUnwrappingConnectionProvider() { - final MultiTenantConnectionProvider multiTenantConnectionProvider = serviceRegistry.getService( + final MultiTenantConnectionProvider multiTenantConnectionProvider = serviceRegistry.getService( MultiTenantConnectionProvider.class ); final ConnectionProvider connectionProvider = multiTenantConnectionProvider.unwrap( ConnectionProvider.class ); assertThat( connectionProvider, is( notNullValue() ) ); @@ -62,9 +61,9 @@ public class SchemaBasedMultiTenancyTest extends AbstractSchemaBasedMultiTenancy @Test @TestForIssue(jiraKey = "HHH-11651") public void testUnwrappingAbstractMultiTenantConnectionProvider() { - final MultiTenantConnectionProvider multiTenantConnectionProvider = serviceRegistry.getService( + final MultiTenantConnectionProvider multiTenantConnectionProvider = serviceRegistry.getService( MultiTenantConnectionProvider.class ); - final AbstractMultiTenantConnectionProvider connectionProvider = multiTenantConnectionProvider.unwrap( + final AbstractMultiTenantConnectionProvider connectionProvider = multiTenantConnectionProvider.unwrap( AbstractMultiTenantConnectionProvider.class ); assertThat( connectionProvider, is( notNullValue() ) ); } @@ -72,9 +71,9 @@ public class SchemaBasedMultiTenancyTest extends AbstractSchemaBasedMultiTenancy @Test @TestForIssue(jiraKey = "HHH-11651") public void testUnwrappingMultiTenantConnectionProvider() { - final MultiTenantConnectionProvider multiTenantConnectionProvider = serviceRegistry.getService( + final MultiTenantConnectionProvider multiTenantConnectionProvider = serviceRegistry.getService( MultiTenantConnectionProvider.class ); - final MultiTenantConnectionProvider connectionProvider = multiTenantConnectionProvider.unwrap( + final MultiTenantConnectionProvider connectionProvider = multiTenantConnectionProvider.unwrap( MultiTenantConnectionProvider.class ); assertThat( connectionProvider, is( notNullValue() ) ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/schema/TenantResolverConfigurationTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/schema/TenantResolverConfigurationTest.java index 89704b6d33..5c637c400b 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/schema/TenantResolverConfigurationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/schema/TenantResolverConfigurationTest.java @@ -34,7 +34,7 @@ public class TenantResolverConfigurationTest extends BaseCoreFunctionalTestCase assertSame(currentTenantResolver, sessionFactory().getCurrentTenantIdentifierResolver()); } - private static class TestCurrentTenantIdentifierResolver implements CurrentTenantIdentifierResolver { + private static class TestCurrentTenantIdentifierResolver implements CurrentTenantIdentifierResolver { private String currentTenantIdentifier; @Override @@ -43,7 +43,7 @@ public class TenantResolverConfigurationTest extends BaseCoreFunctionalTestCase } @Override - public String resolveCurrentTenantIdentifier() { + public Object resolveCurrentTenantIdentifier() { return currentTenantIdentifier; } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/tenantid/TenantIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/tenantid/TenantIdTest.java index 7d4e7b9985..d77ed81ca3 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/tenantid/TenantIdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/tenantid/TenantIdTest.java @@ -8,7 +8,6 @@ package org.hibernate.orm.test.tenantid; import org.hibernate.HibernateError; import org.hibernate.PropertyValueException; -import org.hibernate.Session; import org.hibernate.boot.SessionFactoryBuilder; import org.hibernate.boot.spi.MetadataImplementor; import org.hibernate.context.spi.CurrentTenantIdentifierResolver; @@ -21,8 +20,6 @@ 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.engine.spi.SessionImplementor; -import org.hibernate.type.descriptor.DateTimeUtils; import org.hibernate.testing.orm.junit.SkipForDialect; import org.junit.jupiter.api.AfterEach; @@ -62,7 +59,7 @@ public class TenantIdTest implements SessionFactoryProducer { @Override public SessionFactoryImplementor produceSessionFactory(MetadataImplementor model) { final SessionFactoryBuilder sessionFactoryBuilder = model.getSessionFactoryBuilder(); - sessionFactoryBuilder.applyCurrentTenantIdentifierResolver( new CurrentTenantIdentifierResolver() { + sessionFactoryBuilder.applyCurrentTenantIdentifierResolver( new CurrentTenantIdentifierResolver() { @Override public String resolveCurrentTenantIdentifier() { return currentTenant; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/tenantlongid/TenantLongIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/tenantlongid/TenantLongIdTest.java index 4444661f28..9249135bd2 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/tenantlongid/TenantLongIdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/tenantlongid/TenantLongIdTest.java @@ -49,10 +49,10 @@ public class TenantLongIdTest implements SessionFactoryProducer { @Override public SessionFactoryImplementor produceSessionFactory(MetadataImplementor model) { final SessionFactoryBuilder sessionFactoryBuilder = model.getSessionFactoryBuilder(); - sessionFactoryBuilder.applyCurrentTenantIdentifierResolver( new CurrentTenantIdentifierResolver() { + sessionFactoryBuilder.applyCurrentTenantIdentifierResolver( new CurrentTenantIdentifierResolver() { @Override - public String resolveCurrentTenantIdentifier() { - return currentTenant.toString(); + public Long resolveCurrentTenantIdentifier() { + return currentTenant; } @Override public boolean validateExistingCurrentSessions() { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/tenantuuid/TenantUuidTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/tenantuuid/TenantUuidTest.java index 8fff16ddd1..bc8c8a3430 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/tenantuuid/TenantUuidTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/tenantuuid/TenantUuidTest.java @@ -56,10 +56,10 @@ public class TenantUuidTest implements SessionFactoryProducer { @Override public SessionFactoryImplementor produceSessionFactory(MetadataImplementor model) { final SessionFactoryBuilder sessionFactoryBuilder = model.getSessionFactoryBuilder(); - sessionFactoryBuilder.applyCurrentTenantIdentifierResolver( new CurrentTenantIdentifierResolver() { + sessionFactoryBuilder.applyCurrentTenantIdentifierResolver( new CurrentTenantIdentifierResolver() { @Override - public String resolveCurrentTenantIdentifier() { - return currentTenant.toString(); + public UUID resolveCurrentTenantIdentifier() { + return currentTenant; } @Override public boolean validateExistingCurrentSessions() { diff --git a/migration-guide.adoc b/migration-guide.adoc index 15e2a7af60..56be44f5a4 100644 --- a/migration-guide.adoc +++ b/migration-guide.adoc @@ -31,4 +31,14 @@ class Account { } ---- -See the link:{userGuideBase}#soft-delete[User Guide] for details. \ No newline at end of file +See the link:{userGuideBase}#soft-delete[User Guide] for details. + +[[custom-tenant-identifier-type]] +== Custom tenant identifier type + +The `CurrentTenantIdentifierResolver` and `MultiTenantConnectionProvider` SPIs were generified to support custom tenant identifier types. +Both types now accept a type parameter that represents the tenant identifier type. +Methods that were accepting or returning a tenant identifier value of type `String` were changed to use the type variable. + +Applications can migrate by specifying the type parameter `` when extending `CurrentTenantIdentifierResolver` +or one of the `MultiTenantConnectionProvider` subtypes. diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/validation/MockSessionFactory.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/validation/MockSessionFactory.java index d8cf100f57..6d7361a25f 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/validation/MockSessionFactory.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/validation/MockSessionFactory.java @@ -384,7 +384,12 @@ public abstract class MockSessionFactory } @Override - public CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver() { + public CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver() { + return null; + } + + @Override + public JavaType getTenantIdentifierJavaType() { return null; }