some cleanup to DatasourceConnectionProviderImpl and multi-tenant version

Signed-off-by: Gavin King <gavin@hibernate.org>
This commit is contained in:
Gavin King 2024-08-30 19:05:58 +02:00
parent 47c99d5a31
commit a59f2ac6c8
4 changed files with 44 additions and 42 deletions

View File

@ -526,8 +526,9 @@ public interface JdbcSettings extends C3p0Settings, ProxoolSettings, AgroalSetti
/**
* @see javax.sql.DataSource
*
* @deprecated The JPA-standard {@link #JAKARTA_JTA_DATASOURCE} or {@link #JAKARTA_JTA_DATASOURCE} setting
* is now preferred.
* @deprecated The JPA-standard {@value #JAKARTA_JTA_DATASOURCE} or
* {@value #JAKARTA_NON_JTA_DATASOURCE} setting are now
* preferred.
*/
@Deprecated
String DATASOURCE = "hibernate.connection.datasource";

View File

@ -12,7 +12,8 @@ import java.util.Map;
import javax.sql.DataSource;
import org.hibernate.HibernateException;
import org.hibernate.cfg.Environment;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.JdbcSettings;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.engine.jdbc.connections.spi.DatabaseConnectionInfo;
@ -22,13 +23,17 @@ import org.hibernate.service.spi.Configurable;
import org.hibernate.service.spi.InjectService;
import org.hibernate.service.spi.Stoppable;
import static org.hibernate.cfg.JdbcSettings.DATASOURCE;
/**
* A {@link ConnectionProvider} that manages connections from an underlying {@link DataSource}.
* <p>
* The {@link DataSource} to use may be specified by either:<ul>
* <li>injection using {@link #setDataSource}
* <li>declaring the {@link DataSource} instance using the {@value Environment#DATASOURCE} config property
* <li>declaring the JNDI name under which the {@link DataSource} is found via the {@value Environment#DATASOURCE} config property
* <li>injection using {@link #setDataSource},
* <li>passing the {@link DataSource} instance using {@value JdbcSettings#DATASOURCE},
* {@value JdbcSettings#JAKARTA_JTA_DATASOURCE}, or {@value JdbcSettings#JAKARTA_NON_JTA_DATASOURCE}, or
* <li>declaring the JNDI name under which the {@link DataSource} is found via {@value JdbcSettings#DATASOURCE},
* {@value JdbcSettings#JAKARTA_JTA_DATASOURCE}, or {@value JdbcSettings#JAKARTA_NON_JTA_DATASOURCE}.
* </ul>
*
* @author Gavin King
@ -83,7 +88,7 @@ public class DatasourceConnectionProviderImpl implements ConnectionProvider, Con
@Override
public void configure(Map<String, Object> configValues) {
if ( dataSource == null ) {
final Object dataSourceSetting = configValues.get( Environment.DATASOURCE );
final Object dataSourceSetting = configValues.get( DATASOURCE );
if ( dataSourceSetting instanceof DataSource ) {
dataSource = (DataSource) dataSourceSetting;
}
@ -91,7 +96,7 @@ public class DatasourceConnectionProviderImpl implements ConnectionProvider, Con
final String dataSourceJndiName = (String) dataSourceSetting;
if ( dataSourceJndiName == null ) {
throw new HibernateException(
"DataSource to use was not injected nor specified by [" + Environment.DATASOURCE
"DataSource to use was not injected nor specified by [" + DATASOURCE
+ "] configuration property"
);
}
@ -106,8 +111,8 @@ public class DatasourceConnectionProviderImpl implements ConnectionProvider, Con
throw new HibernateException( "Unable to determine appropriate DataSource to use" );
}
user = (String) configValues.get( Environment.USER );
pass = (String) configValues.get( Environment.PASS );
user = (String) configValues.get( AvailableSettings.USER );
pass = (String) configValues.get( AvailableSettings.PASS );
useCredentials = user != null || pass != null;
available = true;
}
@ -150,7 +155,7 @@ public class DatasourceConnectionProviderImpl implements ConnectionProvider, Con
@Override
public String toInfoString() {
return dataSourceJndiName != null
? "\tDatasource JND name [" + dataSourceJndiName + "]"
? "\tDatasource JNDI name [" + dataSourceJndiName + "]"
: "\tProvided DataSource";
}
};

View File

@ -22,19 +22,20 @@ import org.hibernate.service.spi.ServiceRegistryAwareService;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.service.spi.Stoppable;
import static org.hibernate.cfg.JdbcSettings.DATASOURCE;
import static org.hibernate.cfg.MultiTenancySettings.TENANT_IDENTIFIER_TO_USE_FOR_ANY_KEY;
/**
* A concrete implementation of the {@link MultiTenantConnectionProvider} contract bases on a number of
* reasonable assumptions. We assume that:<ul>
* A concrete implementation of the {@link MultiTenantConnectionProvider} contract bases on
* a number of reasonable assumptions. We assume that:<ul>
* <li>
* The {@link DataSource} instances are all available from JNDI named by the tenant identifier relative
* to a single base JNDI context
* The {@link DataSource} instances are all available from JNDI named by the tenant
* identifier relative to a single base JNDI context.
* </li>
* <li>
* {@value AvailableSettings#DATASOURCE} is a string naming either the {@literal any}
* data source or the base JNDI context. If the latter, {@link MultiTenancySettings#TENANT_IDENTIFIER_TO_USE_FOR_ANY_KEY} must
* also be set.
* data source or the base JNDI context. If the latter,
* {@link MultiTenancySettings#TENANT_IDENTIFIER_TO_USE_FOR_ANY_KEY} must also be set.
* </li>
* </ul>
*
@ -44,10 +45,11 @@ public class DataSourceBasedMultiTenantConnectionProviderImpl<T>
extends AbstractDataSourceBasedMultiTenantConnectionProviderImpl<T>
implements ServiceRegistryAwareService, Stoppable {
private Map<T, DataSource> dataSourceMap;
private final Map<T, DataSource> dataSourceMap = new ConcurrentHashMap<>();
private JndiService jndiService;
private T tenantIdentifierForAny;
private String baseJndiNamespace;
private String jndiName;
@Override
protected DataSource selectAnyDataSource() {
@ -65,21 +67,17 @@ public class DataSourceBasedMultiTenantConnectionProviderImpl<T>
}
private Map<T, DataSource> dataSourceMap() {
if ( dataSourceMap == null ) {
dataSourceMap = new ConcurrentHashMap<>();
}
return dataSourceMap;
}
@Override
public void injectServices(ServiceRegistryImplementor serviceRegistry) {
final Object dataSourceConfigValue =
serviceRegistry.requireService( ConfigurationService.class )
.getSettings().get( AvailableSettings.DATASOURCE );
final ConfigurationService configurationService = serviceRegistry.requireService( ConfigurationService.class );
final Object dataSourceConfigValue = configurationService.getSettings().get( DATASOURCE );
if ( !(dataSourceConfigValue instanceof String) ) {
throw new HibernateException( "Improper set up of DataSourceBasedMultiTenantConnectionProviderImpl" );
throw new HibernateException( "illegal value for configuration setting '" + DATASOURCE + "'" );
}
final String jndiName = (String) dataSourceConfigValue;
jndiName = (String) dataSourceConfigValue;
jndiService = serviceRegistry.getService( JndiService.class );
if ( jndiService == null ) {
@ -91,17 +89,18 @@ public class DataSourceBasedMultiTenantConnectionProviderImpl<T>
throw new HibernateException( "JNDI name [" + jndiName + "] could not be resolved" );
}
if ( namedObject instanceof DataSource ) {
if ( namedObject instanceof DataSource datasource ) {
final int loc = jndiName.lastIndexOf( '/' );
this.baseJndiNamespace = jndiName.substring( 0, loc );
this.tenantIdentifierForAny = (T) jndiName.substring( loc + 1 );
dataSourceMap().put( tenantIdentifierForAny, (DataSource) namedObject );
baseJndiNamespace = jndiName.substring( 0, loc );
final String prefix = jndiName.substring(loc + 1);
tenantIdentifierForAny = (T) prefix;
dataSourceMap().put( tenantIdentifierForAny, datasource );
}
else if ( namedObject instanceof Context ) {
this.baseJndiNamespace = jndiName;
this.tenantIdentifierForAny = (T) serviceRegistry.requireService( ConfigurationService.class )
.getSettings()
.get( TENANT_IDENTIFIER_TO_USE_FOR_ANY_KEY );
baseJndiNamespace = jndiName;
final Object configuredTenantId =
configurationService.getSettings().get( TENANT_IDENTIFIER_TO_USE_FOR_ANY_KEY );
tenantIdentifierForAny = (T) configuredTenantId;
if ( tenantIdentifierForAny == null ) {
throw new HibernateException( "JNDI name named a Context, but tenant identifier to use for ANY was not specified" );
}
@ -116,10 +115,7 @@ public class DataSourceBasedMultiTenantConnectionProviderImpl<T>
@Override
public void stop() {
if ( dataSourceMap != null ) {
dataSourceMap.clear();
dataSourceMap = null;
}
dataSourceMap.clear();
}
@Override
@ -139,5 +135,4 @@ public class DataSourceBasedMultiTenantConnectionProviderImpl<T>
}
};
}
}

View File

@ -163,11 +163,12 @@ public class JdbcEnvironmentInitiator implements StandardServiceInitiator<JdbcEn
private DatabaseConnectionInfo buildDbInfo(ServiceRegistryImplementor registry, Dialect dialect) {
if ( !isMultiTenancyEnabled( registry ) ) {
final ConnectionProvider cp = registry.requireService( ConnectionProvider.class );
return cp.getDatabaseConnectionInfo( dialect );
return registry.requireService( ConnectionProvider.class )
.getDatabaseConnectionInfo( dialect );
}
else {
final MultiTenantConnectionProvider<?> mcp = registry.requireService( MultiTenantConnectionProvider.class );
final MultiTenantConnectionProvider<?> mcp =
registry.requireService( MultiTenantConnectionProvider.class );
return mcp.getDatabaseConnectionInfo( dialect );
}
}