hibernate/hibernate-orm · critical · HibernateException
Could not locate JndiService from DataSourceBasedMultiTenant
Error message
Could not locate JndiService from DataSourceBasedMultiTenantConnectionProviderImpl
What it means
During service injection, DataSourceBasedMultiTenantConnectionProviderImpl asks the ServiceRegistry for JndiService and gets null. JndiService is only available when JNDI integration is configured (typically via hibernate.jndi.* properties in a Java SE environment, or automatically inside an application server). Without it the provider cannot perform any JNDI lookups and fails fast at bootstrap.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/DataSourceBasedMultiTenantConnectionProviderImpl.java:83
return dataSource;
}
private Map<T, DataSource> dataSourceMap() {
return dataSourceMap;
}
@Override
public void injectServices(@Nonnull ServiceRegistryImplementor serviceRegistry) {
final var configurationService = serviceRegistry.requireService( ConfigurationService.class );
final Object dataSourceConfigValue = configurationService.getSettings().get( DATASOURCE );
if ( !(dataSourceConfigValue instanceof String configuredJndiName) ) {
throw new HibernateException( "illegal value for configuration setting '" + DATASOURCE + "'" );
}
jndiName = configuredJndiName;
jndiService = serviceRegistry.getService( JndiService.class );
if ( jndiService == null ) {
throw new HibernateException( "Could not locate JndiService from DataSourceBasedMultiTenantConnectionProviderImpl" );
}
final Object namedObject = jndiService.locate( this.jndiName );
if ( namedObject == null ) {
throw new HibernateException( "JNDI name [" + this.jndiName + "] could not be resolved" );
}
else if ( namedObject instanceof DataSource datasource ) {
final int loc = jndiName.lastIndexOf( '/' );
baseJndiNamespace = jndiName.substring( 0, loc );
final String prefix = jndiName.substring( loc + 1);
tenantIdentifierForAny = (T) prefix;
dataSourceMap().put( tenantIdentifierForAny, datasource );
}
else if ( namedObject instanceof Context ) {
baseJndiNamespace = jndiName;
final Object configuredTenantId =
configurationService.getSettings().get( TENANT_IDENTIFIER_TO_USE_FOR_ANY_KEY );
tenantIdentifierForAny = (T) configuredTenantId;View on GitHub (pinned to fad1729dce)
Solutions
- Configure JNDI for SE use by setting the InitialContext factory (e.g. hibernate.jndi.class=org.osjava.sj.SimpleJndiContextFactory or your server's factory plus hibernate.jndi.url) so JndiService gets built
- Prefer an embedded JNDI implementation (simple-jndi, naming-java) for tests
- If JNDI is not an option, use a custom MultiTenantConnectionProvider that resolves DataSources from a map instead of JNDI
Example fix
// before (no JNDI config in SE)
Map<String,Object> cfg = Map.of(
MultiTenancySettings.MULTI_TENANT_CONNECTION_PROVIDER, DataSourceBasedMultiTenantConnectionProviderImpl.class
); // -> Could not locate JndiService
// after
Map<String,Object> cfg = Map.of(
MultiTenancySettings.MULTI_TENANT_CONNECTION_PROVIDER, DataSourceBasedMultiTenantConnectionProviderImpl.class,
AvailableSettings.DATASOURCE, "java:/datasources/tenants",
AvailableSettings.JNDI_CLASS, "org.osjava.sj.SimpleJndiContextFactory",
AvailableSettings.JNDI_URL, "target/test-classes/simple-jndi"
); Defensive patterns
Strategy: validation
Validate before calling
// pre-flight: can we get an InitialContext at all in this environment?
try {
new InitialContext().getEnvironment();
} catch (NamingException e) {
throw new IllegalStateException(
"JNDI unavailable - configure hibernate.jndi.class or drop the JNDI-based multi-tenant provider", e);
} Prevention
- In Java SE or tests, always pair the JNDI-based provider with hibernate.jndi.class (+ hibernate.jndi.url) pointing at an embedded naming implementation
- In CI, use simple-jndi or a custom JndiService instead of assuming container JNDI
When it happens
Trigger: Bootstrap with DataSourceBasedMultiTenantConnectionProviderImpl in a plain Java SE environment or unit test where no JNDI provider is set up, so serviceRegistry.getService(JndiService.class) returns null.
Common situations: Running multitenancy unit tests outside a container; SE apps that forgot the hibernate.jndi.class / InitialContext factory settings; the JndiService initiator not registering a service because hibernate.jndi.class was not resolvable.
Related errors
- illegal value for configuration setting 'hibernate.connectio
- JNDI name [{}] could not be resolved
- Bootstrap registry should only contain provided services
- No ServiceRegistry was passed to Configuration#buildSessionF
- JNDI name named a Context, but tenant identifier to use for
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/d4b1c143521356a3.
Report an issue: GitHub.