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 * @see javax.sql.DataSource
* *
* @deprecated The JPA-standard {@link #JAKARTA_JTA_DATASOURCE} or {@link #JAKARTA_JTA_DATASOURCE} setting * @deprecated The JPA-standard {@value #JAKARTA_JTA_DATASOURCE} or
* is now preferred. * {@value #JAKARTA_NON_JTA_DATASOURCE} setting are now
* preferred.
*/ */
@Deprecated @Deprecated
String DATASOURCE = "hibernate.connection.datasource"; String DATASOURCE = "hibernate.connection.datasource";

View File

@ -12,7 +12,8 @@ import java.util.Map;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.hibernate.HibernateException; 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.dialect.Dialect;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.engine.jdbc.connections.spi.DatabaseConnectionInfo; 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.InjectService;
import org.hibernate.service.spi.Stoppable; import org.hibernate.service.spi.Stoppable;
import static org.hibernate.cfg.JdbcSettings.DATASOURCE;
/** /**
* A {@link ConnectionProvider} that manages connections from an underlying {@link DataSource}. * A {@link ConnectionProvider} that manages connections from an underlying {@link DataSource}.
* <p> * <p>
* The {@link DataSource} to use may be specified by either:<ul> * The {@link DataSource} to use may be specified by either:<ul>
* <li>injection using {@link #setDataSource} * <li>injection using {@link #setDataSource},
* <li>declaring the {@link DataSource} instance using the {@value Environment#DATASOURCE} config property * <li>passing the {@link DataSource} instance using {@value JdbcSettings#DATASOURCE},
* <li>declaring the JNDI name under which the {@link DataSource} is found via the {@value Environment#DATASOURCE} config property * {@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> * </ul>
* *
* @author Gavin King * @author Gavin King
@ -83,7 +88,7 @@ public class DatasourceConnectionProviderImpl implements ConnectionProvider, Con
@Override @Override
public void configure(Map<String, Object> configValues) { public void configure(Map<String, Object> configValues) {
if ( dataSource == null ) { if ( dataSource == null ) {
final Object dataSourceSetting = configValues.get( Environment.DATASOURCE ); final Object dataSourceSetting = configValues.get( DATASOURCE );
if ( dataSourceSetting instanceof DataSource ) { if ( dataSourceSetting instanceof DataSource ) {
dataSource = (DataSource) dataSourceSetting; dataSource = (DataSource) dataSourceSetting;
} }
@ -91,7 +96,7 @@ public class DatasourceConnectionProviderImpl implements ConnectionProvider, Con
final String dataSourceJndiName = (String) dataSourceSetting; final String dataSourceJndiName = (String) dataSourceSetting;
if ( dataSourceJndiName == null ) { if ( dataSourceJndiName == null ) {
throw new HibernateException( 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" + "] configuration property"
); );
} }
@ -106,8 +111,8 @@ public class DatasourceConnectionProviderImpl implements ConnectionProvider, Con
throw new HibernateException( "Unable to determine appropriate DataSource to use" ); throw new HibernateException( "Unable to determine appropriate DataSource to use" );
} }
user = (String) configValues.get( Environment.USER ); user = (String) configValues.get( AvailableSettings.USER );
pass = (String) configValues.get( Environment.PASS ); pass = (String) configValues.get( AvailableSettings.PASS );
useCredentials = user != null || pass != null; useCredentials = user != null || pass != null;
available = true; available = true;
} }
@ -150,7 +155,7 @@ public class DatasourceConnectionProviderImpl implements ConnectionProvider, Con
@Override @Override
public String toInfoString() { public String toInfoString() {
return dataSourceJndiName != null return dataSourceJndiName != null
? "\tDatasource JND name [" + dataSourceJndiName + "]" ? "\tDatasource JNDI name [" + dataSourceJndiName + "]"
: "\tProvided DataSource"; : "\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.ServiceRegistryImplementor;
import org.hibernate.service.spi.Stoppable; 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; 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 * A concrete implementation of the {@link MultiTenantConnectionProvider} contract bases on
* reasonable assumptions. We assume that:<ul> * a number of reasonable assumptions. We assume that:<ul>
* <li> * <li>
* The {@link DataSource} instances are all available from JNDI named by the tenant identifier relative * The {@link DataSource} instances are all available from JNDI named by the tenant
* to a single base JNDI context * identifier relative to a single base JNDI context.
* </li> * </li>
* <li> * <li>
* {@value AvailableSettings#DATASOURCE} is a string naming either the {@literal any} * {@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 * data source or the base JNDI context. If the latter,
* also be set. * {@link MultiTenancySettings#TENANT_IDENTIFIER_TO_USE_FOR_ANY_KEY} must also be set.
* </li> * </li>
* </ul> * </ul>
* *
@ -44,10 +45,11 @@ public class DataSourceBasedMultiTenantConnectionProviderImpl<T>
extends AbstractDataSourceBasedMultiTenantConnectionProviderImpl<T> extends AbstractDataSourceBasedMultiTenantConnectionProviderImpl<T>
implements ServiceRegistryAwareService, Stoppable { implements ServiceRegistryAwareService, Stoppable {
private Map<T, DataSource> dataSourceMap; private final Map<T, DataSource> dataSourceMap = new ConcurrentHashMap<>();
private JndiService jndiService; private JndiService jndiService;
private T tenantIdentifierForAny; private T tenantIdentifierForAny;
private String baseJndiNamespace; private String baseJndiNamespace;
private String jndiName;
@Override @Override
protected DataSource selectAnyDataSource() { protected DataSource selectAnyDataSource() {
@ -65,21 +67,17 @@ public class DataSourceBasedMultiTenantConnectionProviderImpl<T>
} }
private Map<T, DataSource> dataSourceMap() { private Map<T, DataSource> dataSourceMap() {
if ( dataSourceMap == null ) {
dataSourceMap = new ConcurrentHashMap<>();
}
return dataSourceMap; return dataSourceMap;
} }
@Override @Override
public void injectServices(ServiceRegistryImplementor serviceRegistry) { public void injectServices(ServiceRegistryImplementor serviceRegistry) {
final Object dataSourceConfigValue = final ConfigurationService configurationService = serviceRegistry.requireService( ConfigurationService.class );
serviceRegistry.requireService( ConfigurationService.class ) final Object dataSourceConfigValue = configurationService.getSettings().get( DATASOURCE );
.getSettings().get( AvailableSettings.DATASOURCE );
if ( !(dataSourceConfigValue instanceof String) ) { 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 ); jndiService = serviceRegistry.getService( JndiService.class );
if ( jndiService == null ) { if ( jndiService == null ) {
@ -91,17 +89,18 @@ public class DataSourceBasedMultiTenantConnectionProviderImpl<T>
throw new HibernateException( "JNDI name [" + jndiName + "] could not be resolved" ); throw new HibernateException( "JNDI name [" + jndiName + "] could not be resolved" );
} }
if ( namedObject instanceof DataSource ) { if ( namedObject instanceof DataSource datasource ) {
final int loc = jndiName.lastIndexOf( '/' ); final int loc = jndiName.lastIndexOf( '/' );
this.baseJndiNamespace = jndiName.substring( 0, loc ); baseJndiNamespace = jndiName.substring( 0, loc );
this.tenantIdentifierForAny = (T) jndiName.substring( loc + 1 ); final String prefix = jndiName.substring(loc + 1);
dataSourceMap().put( tenantIdentifierForAny, (DataSource) namedObject ); tenantIdentifierForAny = (T) prefix;
dataSourceMap().put( tenantIdentifierForAny, datasource );
} }
else if ( namedObject instanceof Context ) { else if ( namedObject instanceof Context ) {
this.baseJndiNamespace = jndiName; baseJndiNamespace = jndiName;
this.tenantIdentifierForAny = (T) serviceRegistry.requireService( ConfigurationService.class ) final Object configuredTenantId =
.getSettings() configurationService.getSettings().get( TENANT_IDENTIFIER_TO_USE_FOR_ANY_KEY );
.get( TENANT_IDENTIFIER_TO_USE_FOR_ANY_KEY ); tenantIdentifierForAny = (T) configuredTenantId;
if ( tenantIdentifierForAny == null ) { if ( tenantIdentifierForAny == null ) {
throw new HibernateException( "JNDI name named a Context, but tenant identifier to use for ANY was not specified" ); 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 @Override
public void stop() { public void stop() {
if ( dataSourceMap != null ) { dataSourceMap.clear();
dataSourceMap.clear();
dataSourceMap = null;
}
} }
@Override @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) { private DatabaseConnectionInfo buildDbInfo(ServiceRegistryImplementor registry, Dialect dialect) {
if ( !isMultiTenancyEnabled( registry ) ) { if ( !isMultiTenancyEnabled( registry ) ) {
final ConnectionProvider cp = registry.requireService( ConnectionProvider.class ); return registry.requireService( ConnectionProvider.class )
return cp.getDatabaseConnectionInfo( dialect ); .getDatabaseConnectionInfo( dialect );
} }
else { else {
final MultiTenantConnectionProvider<?> mcp = registry.requireService( MultiTenantConnectionProvider.class ); final MultiTenantConnectionProvider<?> mcp =
registry.requireService( MultiTenantConnectionProvider.class );
return mcp.getDatabaseConnectionInfo( dialect ); return mcp.getDatabaseConnectionInfo( dialect );
} }
} }