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

  1. 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
  2. Prefer an embedded JNDI implementation (simple-jndi, naming-java) for tests
  3. 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

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


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/d4b1c143521356a3. Report an issue: GitHub.