Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions documentation/src/main/asciidoc/introduction/Advanced.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ To make use of multi-tenancy, we'll usually need to set at least one of these co
| `hibernate.multi_tenant_connection_provider` | Specifies the `MultiTenantConnectionProvider`
|===

Do not configure those properties if you would like the configured `BeanContainer` provide the implementation.
A longer discussion of multi-tenancy may be found in the {multitenacy-doc}[User Guide].

[[custom-sql]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ The `MultiTenantConnectionProvider` to use can be specified in a number of ways:

* Use the `hibernate.multi_tenant_connection_provider` setting.
It could name a `MultiTenantConnectionProvider` instance, a `MultiTenantConnectionProvider` implementation class reference or a `MultiTenantConnectionProvider` implementation class name.
* Provided by the configured `BeanContainer`.
* Passed directly to the `org.hibernate.boot.registry.StandardServiceRegistryBuilder`.
* If none of the above options match, but the settings do specify a `hibernate.connection.datasource` value,
Hibernate will assume it should use the specific `DataSourceBasedMultiTenantConnectionProviderImpl` implementation which works on a number of pretty reasonable assumptions when running inside of an app server and using one `javax.sql.DataSource` per tenant.
Expand Down Expand Up @@ -161,7 +162,7 @@ include::{example-dir-multitenancy}/AbstractMultiTenancyTest.java[tags=multitena

`org.hibernate.context.spi.CurrentTenantIdentifierResolver` is a contract for Hibernate to be able to resolve what the application considers the current tenant identifier.
The implementation to use can be either passed directly to `Configuration` via its `setCurrentTenantIdentifierResolver` method,
or be specified via the `hibernate.tenant_identifier_resolver` setting.
or be specified via the `hibernate.tenant_identifier_resolver` setting, or be provided by the configured `BeanContainer`.

There are two situations where `CurrentTenantIdentifierResolver` is used:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.query.sqm.sql.SqmTranslatorFactory;
import org.hibernate.resource.beans.container.spi.BeanContainer;
import org.hibernate.resource.beans.internal.Helper;
import org.hibernate.resource.beans.spi.BeanInstanceProducer;
import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
import org.hibernate.resource.jdbc.spi.StatementInspector;
import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder;
Expand Down Expand Up @@ -283,6 +287,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
private final int queryStatisticsMaxSize;


@SuppressWarnings( "unchecked" )
public SessionFactoryOptionsBuilder(StandardServiceRegistry serviceRegistry, BootstrapContext context) {
this.serviceRegistry = serviceRegistry;
this.jpaBootstrap = context.isJpaBootstrap();
Expand Down Expand Up @@ -372,6 +377,38 @@ public SessionFactoryOptionsBuilder(StandardServiceRegistry serviceRegistry, Boo
CurrentTenantIdentifierResolver.class,
configurationSettings.get( MULTI_TENANT_IDENTIFIER_RESOLVER )
);
if ( this.currentTenantIdentifierResolver == null ) {
final BeanContainer beanContainer = Helper.allowExtensionsInCdi( serviceRegistry ) ? serviceRegistry.requireService( ManagedBeanRegistry.class ).getBeanContainer() : null;
if (beanContainer != null) {
this.currentTenantIdentifierResolver = beanContainer.getBean(
CurrentTenantIdentifierResolver.class,
new BeanContainer.LifecycleOptions() {
@Override
public boolean canUseCachedReferences() {
return true;
}

@Override
public boolean useJpaCompliantCreation() {
return false;
}
},
new BeanInstanceProducer() {

@Override
public <B> B produceBeanInstance(Class<B> beanType) {
return null;
}

@Override
public <B> B produceBeanInstance(String name, Class<B> beanType) {
return null;
}

}
).getBeanInstance();
}
Comment on lines +381 to +410
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please extract out a function for this, the constructor is already waaaaaay too long.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gavinking I suggest to introduce new util methods for org.hibernate.resource.beans.internal.Helper and refactor all BeanContainer access to use those methods in another PR, WDYT?

	@SuppressWarnings( "unchecked" )
	public static <T> T getBean(ServiceRegistry serviceRegistry, Class<?> beanType, boolean canUseCachedReferences, boolean useJpaCompliantCreation, T fallback) {
		final BeanContainer beanContainer = allowExtensionsInCdi( serviceRegistry ) ? serviceRegistry.requireService( ManagedBeanRegistry.class ).getBeanContainer() : null;
		if ( beanContainer == null ) {
			return null;
		}
		return (T) beanContainer.getBean(
				beanType,
				new BeanContainer.LifecycleOptions() {
					@Override
					public boolean canUseCachedReferences() {
						return canUseCachedReferences;
					}

					@Override
					public boolean useJpaCompliantCreation() {
						return useJpaCompliantCreation;
					}
				},
				new BeanInstanceProducer() {

					@Override
					public <B> B produceBeanInstance(Class<B> beanType) {
						return (B) fallback;
					}

					@Override
					public <B> B produceBeanInstance(String name, Class<B> beanType) {
						throw new UnsupportedOperationException("The method shouldn't be called");
					}

				}
		).getBeanInstance();
	}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest to introduce new util methods for org.hibernate.resource.beans.internal.Helper and refactor all BeanContainer access to use those methods

That sounds fine to me, but please do it now, so that it doesn't get forgotten. Thanks.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, great, thanks 🙏

}

this.delayBatchFetchLoaderCreations = configurationService.getSetting( DELAY_ENTITY_LOADER_CREATIONS, BOOLEAN, true );
this.defaultBatchFetchSize = getInt( DEFAULT_BATCH_FETCH_SIZE, configurationSettings, -1 );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/
package org.hibernate.engine.jdbc.connections.internal;

import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.util.Collection;
import java.util.HashSet;
Expand All @@ -19,6 +20,11 @@
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.resource.beans.container.spi.BeanContainer;
import org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer;
import org.hibernate.resource.beans.internal.Helper;
import org.hibernate.resource.beans.spi.BeanInstanceProducer;
import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
import org.hibernate.service.spi.ServiceRegistryImplementor;

import static java.sql.Connection.TRANSACTION_NONE;
Expand Down Expand Up @@ -102,6 +108,7 @@ public ConnectionProvider initiateService(
return null;
}

final BeanContainer beanContainer = Helper.allowExtensionsInCdi( registry ) ? registry.requireService( ManagedBeanRegistry.class ).getBeanContainer() : null;
final StrategySelector strategySelector = registry.requireService( StrategySelector.class );
final Object explicitSetting = configurationValues.get( CONNECTION_PROVIDER );
if ( explicitSetting != null ) {
Expand All @@ -111,25 +118,25 @@ public ConnectionProvider initiateService(
}
else if ( explicitSetting instanceof Class<?> providerClass ) {
LOG.instantiatingExplicitConnectionProvider( providerClass.getName() );
return instantiateExplicitConnectionProvider( providerClass );
return instantiateExplicitConnectionProvider( providerClass, beanContainer );
}
else {
final String providerName = nullIfEmpty( explicitSetting.toString() );
if ( providerName != null ) {
return instantiateNamedConnectionProvider(providerName, strategySelector);
return instantiateNamedConnectionProvider(providerName, strategySelector, beanContainer);
}
}
}

return instantiateConnectionProvider( configurationValues, strategySelector );
return instantiateConnectionProvider( configurationValues, strategySelector, beanContainer );
}

private ConnectionProvider instantiateNamedConnectionProvider(String providerName, StrategySelector strategySelector) {
private ConnectionProvider instantiateNamedConnectionProvider(String providerName, StrategySelector strategySelector, BeanContainer beanContainer) {
LOG.instantiatingExplicitConnectionProvider( providerName );
final Class<?> providerClass =
strategySelector.selectStrategyImplementor( ConnectionProvider.class, providerName );
try {
return instantiateExplicitConnectionProvider( providerClass );
return instantiateExplicitConnectionProvider( providerClass, beanContainer );
}
catch (Exception e) {
throw new HibernateException(
Expand All @@ -140,7 +147,7 @@ private ConnectionProvider instantiateNamedConnectionProvider(String providerNam
}

private ConnectionProvider instantiateConnectionProvider(
Map<String, Object> configurationValues, StrategySelector strategySelector) {
Map<String, Object> configurationValues, StrategySelector strategySelector, BeanContainer beanContainer) {
if ( configurationValues.containsKey( DATASOURCE ) ) {
return new DatasourceConnectionProviderImpl();
}
Expand All @@ -149,9 +156,9 @@ private ConnectionProvider instantiateConnectionProvider(
getSingleRegisteredProvider( strategySelector );
if ( singleRegisteredProvider != null ) {
try {
return singleRegisteredProvider.newInstance();
return singleRegisteredProvider.getConstructor().newInstance();
}
catch (IllegalAccessException | InstantiationException e) {
catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException | InstantiationException e) {
throw new HibernateException( "Could not instantiate singular-registered ConnectionProvider", e );
}
}
Expand All @@ -177,11 +184,47 @@ else if ( configurationValues.containsKey( URL ) ) {
return new DriverManagerConnectionProviderImpl();
}
else {
LOG.noAppropriateConnectionProvider();
return new UserSuppliedConnectionProviderImpl();
if (beanContainer != null) {
return beanContainer.getBean(
ConnectionProvider.class,
new BeanContainer.LifecycleOptions() {
@Override
public boolean canUseCachedReferences() {
return true;
}

@Override
public boolean useJpaCompliantCreation() {
return true;
}
},
new BeanInstanceProducer() {

@Override
public <B> B produceBeanInstance(Class<B> beanType) {
return (B) noAppropriateConnectionProvider();
}

@Override
public <B> B produceBeanInstance(String name, Class<B> beanType) {
return (B) noAppropriateConnectionProvider();
}

}
).getBeanInstance();
}
else {
return noAppropriateConnectionProvider();
}

}
Comment on lines +188 to 220
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, please extract a function.

}

private ConnectionProvider noAppropriateConnectionProvider() {
LOG.noAppropriateConnectionProvider();
return new UserSuppliedConnectionProviderImpl();
}

private Class<? extends ConnectionProvider> getSingleRegisteredProvider(StrategySelector strategySelector) {
final Collection<Class<? extends ConnectionProvider>> implementors =
strategySelector.getRegisteredStrategyImplementors( ConnectionProvider.class );
Expand All @@ -190,9 +233,28 @@ private Class<? extends ConnectionProvider> getSingleRegisteredProvider(Strategy
: null;
}

private ConnectionProvider instantiateExplicitConnectionProvider(Class<?> providerClass) {
private ConnectionProvider instantiateExplicitConnectionProvider(Class<?> providerClass, BeanContainer beanContainer) {
try {
return (ConnectionProvider) providerClass.newInstance();
if ( beanContainer != null ) {
return (ConnectionProvider) beanContainer.getBean(
providerClass,
new BeanContainer.LifecycleOptions() {
@Override
public boolean canUseCachedReferences() {
return true;
}

@Override
public boolean useJpaCompliantCreation() {
return true;
}
},
FallbackBeanInstanceProducer.INSTANCE
).getBeanInstance();
}
else {
return (ConnectionProvider) providerClass.getConstructor().newInstance();
}
}
catch (Exception e) {
throw new HibernateException( "Could not instantiate connection provider [" + providerClass.getName() + "]", e );
Expand All @@ -201,7 +263,7 @@ private ConnectionProvider instantiateExplicitConnectionProvider(Class<?> provid

private static ConnectionProvider instantiateProvider(StrategySelector selector, String strategy) {
try {
return selector.selectStrategyImplementor( ConnectionProvider.class, strategy ).newInstance();
return selector.selectStrategyImplementor( ConnectionProvider.class, strategy ).getConstructor().newInstance();
}
catch ( Exception e ) {
LOG.providerClassNotFound(strategy);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.jdbc.connections.spi.DataSourceBasedMultiTenantConnectionProviderImpl;
import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider;
import org.hibernate.resource.beans.container.spi.BeanContainer;
import org.hibernate.resource.beans.internal.Helper;
import org.hibernate.resource.beans.spi.BeanInstanceProducer;
import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
import org.hibernate.service.spi.ServiceException;
import org.hibernate.service.spi.ServiceRegistryImplementor;

Expand Down Expand Up @@ -39,7 +43,36 @@ public Class<MultiTenantConnectionProvider<?>> getServiceInitiated() {
@Override
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.
final BeanContainer beanContainer = Helper.allowExtensionsInCdi( registry ) ? registry.requireService( ManagedBeanRegistry.class ).getBeanContainer() : null;
if (beanContainer != null) {
return beanContainer.getBean(
MultiTenantConnectionProvider.class,
new BeanContainer.LifecycleOptions() {
@Override
public boolean canUseCachedReferences() {
return true;
}

@Override
public boolean useJpaCompliantCreation() {
return true;
}
},
new BeanInstanceProducer() {

@Override
public <B> B produceBeanInstance(Class<B> beanType) {
return null;
}

@Override
public <B> B produceBeanInstance(String name, Class<B> beanType) {
return null;
}

}
).getBeanInstance();
}
Comment on lines +46 to +75
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, extract a function here, please.

return null;
}

Expand Down
Loading
Loading