HHH-14822 Support custom tenant identifier type
This commit is contained in:
parent
d3674186bf
commit
5ea40e255d
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -55,23 +55,7 @@ public class TenantIdBinder implements AttributeBinder<TenantId> {
|
|||
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 {
|
||||
|
@ -90,7 +74,7 @@ public class TenantIdBinder implements AttributeBinder<TenantId> {
|
|||
}
|
||||
persistentClass.addFilter(
|
||||
FILTER_NAME,
|
||||
columnNameOrFormula(property)
|
||||
columnNameOrFormula( property )
|
||||
+ " = :"
|
||||
+ PARAMETER_NAME,
|
||||
true,
|
||||
|
@ -98,18 +82,18 @@ public class TenantIdBinder implements AttributeBinder<TenantId> {
|
|||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -206,7 +206,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
|
|||
|
||||
// multi-tenancy
|
||||
private boolean multiTenancyEnabled;
|
||||
private CurrentTenantIdentifierResolver currentTenantIdentifierResolver;
|
||||
private CurrentTenantIdentifierResolver<Object> currentTenantIdentifierResolver;
|
||||
|
||||
// Queries
|
||||
private SqmFunctionRegistry sqmFunctionRegistry;
|
||||
|
@ -1008,7 +1008,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver() {
|
||||
public CurrentTenantIdentifierResolver<Object> 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<Object>) resolver;
|
||||
}
|
||||
|
||||
public void enableNamedQueryCheckingOnStartup(boolean enabled) {
|
||||
|
|
|
@ -210,7 +210,7 @@ public abstract class AbstractDelegatingSessionFactoryBuilder<T extends SessionF
|
|||
}
|
||||
|
||||
@Override
|
||||
public T applyCurrentTenantIdentifierResolver(CurrentTenantIdentifierResolver resolver) {
|
||||
public T applyCurrentTenantIdentifierResolver(CurrentTenantIdentifierResolver<?> resolver) {
|
||||
delegate.applyCurrentTenantIdentifierResolver( resolver );
|
||||
return getThis();
|
||||
}
|
||||
|
|
|
@ -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<Object> getCurrentTenantIdentifierResolver() {
|
||||
return delegate.getCurrentTenantIdentifierResolver();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaType<Object> getDefaultTenantIdentifierJavaType() {
|
||||
return delegate.getDefaultTenantIdentifierJavaType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isJtaTrackByThread() {
|
||||
return delegate.isJtaTrackByThread();
|
||||
|
|
|
@ -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<Object> 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<Object> getDefaultTenantIdentifierJavaType() {
|
||||
return ObjectJavaType.INSTANCE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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<String> toString;
|
||||
|
||||
public static class NaturalIdCacheKeyBuilder implements MutableCacheKeyBuilder {
|
||||
|
||||
private final String entityName;
|
||||
private final String tenantIdentifier;
|
||||
|
||||
private final List<Object> 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<Object> 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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<String> 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;
|
||||
|
|
|
@ -155,7 +155,7 @@ public class Configuration {
|
|||
private Interceptor interceptor;
|
||||
private SessionFactoryObserver sessionFactoryObserver;
|
||||
private StatementInspector statementInspector;
|
||||
private CurrentTenantIdentifierResolver currentTenantIdentifierResolver;
|
||||
private CurrentTenantIdentifierResolver<Object> 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<Object> 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<Object> currentTenantIdentifierResolver) {
|
||||
this.currentTenantIdentifierResolver = currentTenantIdentifierResolver;
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -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<Object> 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<Object> 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<Object> 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() )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -20,13 +20,13 @@ package org.hibernate.context.spi;
|
|||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface CurrentTenantIdentifierResolver {
|
||||
public interface CurrentTenantIdentifierResolver<T> {
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ import org.jboss.logging.Logger;
|
|||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class MultiTenantConnectionProviderInitiator implements StandardServiceInitiator<MultiTenantConnectionProvider> {
|
||||
public class MultiTenantConnectionProviderInitiator implements StandardServiceInitiator<MultiTenantConnectionProvider<?>> {
|
||||
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<MultiTenantConnectionProvider> getServiceInitiated() {
|
||||
return MultiTenantConnectionProvider.class;
|
||||
public Class<MultiTenantConnectionProvider<?>> getServiceInitiated() {
|
||||
//noinspection unchecked
|
||||
return (Class<MultiTenantConnectionProvider<?>>) (Class<?>) MultiTenantConnectionProvider.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiTenantConnectionProvider initiateService(Map<String, Object> configurationValues, ServiceRegistryImplementor registry) {
|
||||
public MultiTenantConnectionProvider<?> initiateService(Map<String, Object> 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<MultiTenantConnectionProvider> implClass;
|
||||
final Class<MultiTenantConnectionProvider<?>> implClass;
|
||||
if ( configValue instanceof Class ) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<MultiTenantConnectionProvider> clazz = (Class<MultiTenantConnectionProvider>) configValue;
|
||||
Class<MultiTenantConnectionProvider<?>> clazz = (Class<MultiTenantConnectionProvider<?>>) configValue;
|
||||
implClass = clazz;
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -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<T> implements MultiTenantConnectionProvider<T> {
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -20,9 +20,9 @@ import org.hibernate.service.UnknownUnwrapTypeException;
|
|||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public abstract class AbstractMultiTenantConnectionProvider implements MultiTenantConnectionProvider {
|
||||
public abstract class AbstractMultiTenantConnectionProvider<T> implements MultiTenantConnectionProvider<T> {
|
||||
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 );
|
||||
}
|
||||
|
||||
|
|
|
@ -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<T>
|
||||
extends AbstractDataSourceBasedMultiTenantConnectionProviderImpl<T>
|
||||
implements ServiceRegistryAwareService, Stoppable {
|
||||
|
||||
private Map<String,DataSource> dataSourceMap;
|
||||
private Map<T, DataSource> 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<String,DataSource> dataSourceMap() {
|
||||
private Map<T, DataSource> 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 ) {
|
||||
|
|
|
@ -22,12 +22,14 @@ import org.hibernate.service.spi.Wrapped;
|
|||
* An application usually implements its own custom {@code MultiTenantConnectionProvider}
|
||||
* by subclassing {@link AbstractMultiTenantConnectionProvider}.
|
||||
*
|
||||
* @param <T> 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<T> 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
|
||||
|
|
|
@ -392,7 +392,7 @@ public class JdbcEnvironmentInitiator implements StandardServiceInitiator<JdbcEn
|
|||
return new ConnectionProviderJdbcConnectionAccess( connectionProvider );
|
||||
}
|
||||
else {
|
||||
final MultiTenantConnectionProvider multiTenantConnectionProvider = registry.getService( MultiTenantConnectionProvider.class );
|
||||
final MultiTenantConnectionProvider<?> multiTenantConnectionProvider = registry.getService( MultiTenantConnectionProvider.class );
|
||||
return new MultiTenantConnectionProviderJdbcConnectionAccess( multiTenantConnectionProvider );
|
||||
}
|
||||
}
|
||||
|
@ -403,7 +403,7 @@ public class JdbcEnvironmentInitiator implements StandardServiceInitiator<JdbcEn
|
|||
return new ConnectionProviderJdbcConnectionAccess( connectionProvider );
|
||||
}
|
||||
else {
|
||||
final MultiTenantConnectionProvider multiTenantConnectionProvider = registry.getService( MultiTenantConnectionProvider.class );
|
||||
final MultiTenantConnectionProvider<?> multiTenantConnectionProvider = registry.getService( MultiTenantConnectionProvider.class );
|
||||
return new MultiTenantConnectionProviderJdbcConnectionAccess( multiTenantConnectionProvider );
|
||||
}
|
||||
}
|
||||
|
@ -436,13 +436,13 @@ public class JdbcEnvironmentInitiator implements StandardServiceInitiator<JdbcEn
|
|||
}
|
||||
|
||||
public static class MultiTenantConnectionProviderJdbcConnectionAccess implements JdbcConnectionAccess {
|
||||
private final MultiTenantConnectionProvider connectionProvider;
|
||||
private final MultiTenantConnectionProvider<?> connectionProvider;
|
||||
|
||||
public MultiTenantConnectionProviderJdbcConnectionAccess(MultiTenantConnectionProvider connectionProvider) {
|
||||
public MultiTenantConnectionProviderJdbcConnectionAccess(MultiTenantConnectionProvider<?> connectionProvider) {
|
||||
this.connectionProvider = connectionProvider;
|
||||
}
|
||||
|
||||
public MultiTenantConnectionProvider getConnectionProvider() {
|
||||
public MultiTenantConnectionProvider<?> getConnectionProvider() {
|
||||
return connectionProvider;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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<Object> getCurrentTenantIdentifierResolver() {
|
||||
return delegate.getCurrentTenantIdentifierResolver();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaType<Object> getTenantIdentifierJavaType() {
|
||||
return delegate.getTenantIdentifierJavaType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FastSessionServices getFastSessionServices() {
|
||||
return delegate.getFastSessionServices();
|
||||
|
|
|
@ -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<Object> getCurrentTenantIdentifierResolver();
|
||||
|
||||
/**
|
||||
* The java type to use for a tenant identifier.
|
||||
*
|
||||
* @since 6.4
|
||||
*/
|
||||
JavaType<Object> getTenantIdentifierJavaType();
|
||||
|
||||
/**
|
||||
* @return the FastSessionServices instance associated with this SessionFactory
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -73,6 +73,11 @@ public class SharedSessionDelegatorBaseImpl implements SharedSessionContractImpl
|
|||
return delegate.getTenantIdentifier();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTenantIdentifierValue() {
|
||||
return delegate.getTenantIdentifierValue();
|
||||
}
|
||||
|
||||
private QueryProducerImplementor queryDelegate() {
|
||||
return delegate;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Object> 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<Object> 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<Object> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<Object> connectionProvider;
|
||||
private final SharedSessionContractImplementor session;
|
||||
|
||||
|
||||
public ContextualJdbcConnectionAccess(
|
||||
String tenantIdentifier,
|
||||
Object tenantIdentifier,
|
||||
SessionEventListener listener,
|
||||
MultiTenantConnectionProvider connectionProvider,
|
||||
MultiTenantConnectionProvider<Object> connectionProvider,
|
||||
SharedSessionContractImplementor session) {
|
||||
this.tenantIdentifier = tenantIdentifier;
|
||||
this.listener = listener;
|
||||
|
|
|
@ -158,7 +158,7 @@ public final class FastSessionServices {
|
|||
final TimeZoneStorageStrategy defaultTimeZoneStorageStrategy;
|
||||
final boolean requiresMultiTenantConnectionProvider;
|
||||
final ConnectionProvider connectionProvider;
|
||||
final MultiTenantConnectionProvider multiTenantConnectionProvider;
|
||||
final MultiTenantConnectionProvider<Object> multiTenantConnectionProvider;
|
||||
final ClassLoaderService classLoaderService;
|
||||
final TransactionCoordinatorBuilder transactionCoordinatorBuilder;
|
||||
public final JdbcServices jdbcServices;
|
||||
|
|
|
@ -46,6 +46,8 @@ public interface SessionCreationOptions {
|
|||
|
||||
String getTenantIdentifier();
|
||||
|
||||
Object getTenantIdentifierValue();
|
||||
|
||||
TimeZone getJdbcTimeZone();
|
||||
|
||||
/**
|
||||
|
|
|
@ -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<String, Generator> identifierGenerators;
|
||||
private final transient Map<String, FilterDefinition> filters;
|
||||
private final transient Map<String, FetchProfile> fetchProfiles;
|
||||
private final transient JavaType<Object> 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<Object> 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<Object> 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<Object> 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<Object> getCurrentTenantIdentifierResolver() {
|
||||
return getSessionFactoryOptions().getCurrentTenantIdentifierResolver();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaType<Object> getTenantIdentifierJavaType() {
|
||||
return tenantIdentifierJavaType;
|
||||
}
|
||||
|
||||
|
||||
// Serialization handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -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<Object> resolver = factory.getCurrentTenantIdentifierResolver();
|
||||
if ( resolver==null || !resolver.isRoot( tenantIdentifier ) ) {
|
||||
// turn on the filter, unless this is the "root" tenant with access to all partitions
|
||||
loadQueryInfluencers
|
||||
|
@ -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() );
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<Object> 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 );
|
||||
|
|
|
@ -17,7 +17,7 @@ import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
|
|||
*/
|
||||
//tag::multitenacy-hibernate-ConfigurableMultiTenantConnectionProvider-example[]
|
||||
public class ConfigurableMultiTenantConnectionProvider
|
||||
extends AbstractMultiTenantConnectionProvider {
|
||||
extends AbstractMultiTenantConnectionProvider<String> {
|
||||
|
||||
private final Map<String, ConnectionProvider> connectionProviderMap =
|
||||
new HashMap<>();
|
||||
|
|
|
@ -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<String> {
|
||||
private Map<String,ConnectionProvider> connectionProviderMap;
|
||||
|
||||
public TestingConnectionProvider(Map<String, ConnectionProvider> connectionProviderMap) {
|
||||
|
|
|
@ -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<Object> {
|
||||
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.
|
||||
|
|
|
@ -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<T extends MultiTenantConnectionProvider, C extends ConnectionProvider> extends BaseUnitTestCase {
|
||||
public abstract class AbstractSchemaBasedMultiTenancyTest<T extends MultiTenantConnectionProvider<String>, C extends ConnectionProvider> extends BaseUnitTestCase {
|
||||
protected C acmeProvider;
|
||||
protected C jbossProvider;
|
||||
|
||||
|
@ -271,7 +270,7 @@ public abstract class AbstractSchemaBasedMultiTenancyTest<T extends MultiTenantC
|
|||
protected SessionBuilder newSession(String tenant) {
|
||||
return sessionFactory
|
||||
.withOptions()
|
||||
.tenantIdentifier( tenant );
|
||||
.tenantIdentifier( (Object) tenant );
|
||||
}
|
||||
|
||||
private SessionBuilder jboss() {
|
||||
|
|
|
@ -45,7 +45,7 @@ public class CurrentTenantResolverMultiTenancyTest extends SchemaBasedMultiTenan
|
|||
return sessionBuilder;
|
||||
}
|
||||
|
||||
private static class TestCurrentTenantIdentifierResolver implements CurrentTenantIdentifierResolver {
|
||||
private static class TestCurrentTenantIdentifierResolver implements CurrentTenantIdentifierResolver<Object> {
|
||||
private String currentTenantIdentifier;
|
||||
|
||||
@Override
|
||||
|
@ -54,7 +54,7 @@ public class CurrentTenantResolverMultiTenancyTest extends SchemaBasedMultiTenan
|
|||
}
|
||||
|
||||
@Override
|
||||
public String resolveCurrentTenantIdentifier() {
|
||||
public Object resolveCurrentTenantIdentifier() {
|
||||
return currentTenantIdentifier;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String>, ConnectionProvider> {
|
||||
|
||||
protected AbstractDataSourceBasedMultiTenantConnectionProviderImpl buildMultiTenantConnectionProvider() {
|
||||
protected AbstractDataSourceBasedMultiTenantConnectionProviderImpl<String> 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<String> 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<String> 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<String> multiTenantConnectionProvider = serviceRegistry.getService(
|
||||
MultiTenantConnectionProvider.class );
|
||||
final MultiTenantConnectionProvider connectionProvider = multiTenantConnectionProvider.unwrap(
|
||||
final MultiTenantConnectionProvider<String> connectionProvider = multiTenantConnectionProvider.unwrap(
|
||||
MultiTenantConnectionProvider.class );
|
||||
assertThat( connectionProvider, is( notNullValue() ) );
|
||||
}
|
||||
|
|
|
@ -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<String>, ConnectionProvider> {
|
||||
|
||||
protected AbstractMultiTenantConnectionProvider buildMultiTenantConnectionProvider() {
|
||||
protected AbstractMultiTenantConnectionProvider<String> 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<String> 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<String> 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<String> multiTenantConnectionProvider = serviceRegistry.getService(
|
||||
MultiTenantConnectionProvider.class );
|
||||
final MultiTenantConnectionProvider connectionProvider = multiTenantConnectionProvider.unwrap(
|
||||
final MultiTenantConnectionProvider<String> connectionProvider = multiTenantConnectionProvider.unwrap(
|
||||
MultiTenantConnectionProvider.class );
|
||||
assertThat( connectionProvider, is( notNullValue() ) );
|
||||
}
|
||||
|
|
|
@ -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<Object> {
|
||||
private String currentTenantIdentifier;
|
||||
|
||||
@Override
|
||||
|
@ -43,7 +43,7 @@ public class TenantResolverConfigurationTest extends BaseCoreFunctionalTestCase
|
|||
}
|
||||
|
||||
@Override
|
||||
public String resolveCurrentTenantIdentifier() {
|
||||
public Object resolveCurrentTenantIdentifier() {
|
||||
return currentTenantIdentifier;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String>() {
|
||||
@Override
|
||||
public String resolveCurrentTenantIdentifier() {
|
||||
return currentTenant;
|
||||
|
|
|
@ -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<Long>() {
|
||||
@Override
|
||||
public String resolveCurrentTenantIdentifier() {
|
||||
return currentTenant.toString();
|
||||
public Long resolveCurrentTenantIdentifier() {
|
||||
return currentTenant;
|
||||
}
|
||||
@Override
|
||||
public boolean validateExistingCurrentSessions() {
|
||||
|
|
|
@ -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<UUID>() {
|
||||
@Override
|
||||
public String resolveCurrentTenantIdentifier() {
|
||||
return currentTenant.toString();
|
||||
public UUID resolveCurrentTenantIdentifier() {
|
||||
return currentTenant;
|
||||
}
|
||||
@Override
|
||||
public boolean validateExistingCurrentSessions() {
|
||||
|
|
|
@ -32,3 +32,13 @@ class Account {
|
|||
----
|
||||
|
||||
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 `<String>` when extending `CurrentTenantIdentifierResolver`
|
||||
or one of the `MultiTenantConnectionProvider` subtypes.
|
||||
|
|
|
@ -384,7 +384,12 @@ public abstract class MockSessionFactory
|
|||
}
|
||||
|
||||
@Override
|
||||
public CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver() {
|
||||
public CurrentTenantIdentifierResolver<Object> getCurrentTenantIdentifierResolver() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaType<Object> getTenantIdentifierJavaType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue