I am trying to create minimalistic Schema Based - Multitenant - Spring Boot but I am getting following error. Application can be downloaded from GitHub https://github.com/ivoronline/spring_boot_db_multitenant
Error
Unable to instantiate specified multi-tenant connection provider [com.ivoronline.spring_boot_db_multitenant.config.TenantConnectionProvider]
application.properties
#spring.jpa.properties.hibernate.multiTenancy = SCHEMA
spring.jpa.properties.hibernate.multiTenancy = SCHEMA
spring.jpa.properties.hibernate.multi_tenant_connection_provider = com.ivoronline.spring_boot_db_multitenant.config.TenantConnectionProvider
spring.jpa.properties.hibernate.tenant_identifier_resolver = com.ivoronline.spring_boot_db_multitenant.config.TenantIdentifierResolver
# JPA / HIBERNATE
spring.jpa.hibernate.ddl-auto = create
DataSourceConfig.java
@Configuration
public class DataSourceConfig {
@Bean
public DataSource dataSource() {
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.url("jdbc:postgresql://localhost:5432/multitenant");
dataSourceBuilder.username("postgres");
dataSourceBuilder.password("letmein");
dataSourceBuilder.driverClassName("org.postgresql.Driver");
return dataSourceBuilder.build();
}
}
TenantIdentifierResolver.java
@Component
public class TenantIdentifierResolver implements CurrentTenantIdentifierResolver {
static final String DEFAULT_TENANT = "public";
@Override
public String resolveCurrentTenantIdentifier() {
return "public";
}
@Override
public boolean validateExistingCurrentSessions() {
return true;
}
}
TenantConnectionProvider.java
@Component
public class TenantConnectionProvider implements MultiTenantConnectionProvider {
private DataSource datasource;
public TenantConnectionProvider(DataSource dataSource) {
this.datasource = dataSource;
}
@Override
public Connection getAnyConnection() throws SQLException {
return datasource.getConnection();
}
@Override
public void releaseAnyConnection(Connection connection) throws SQLException {
connection.close();
}
@Override
public Connection getConnection(String tenantIdentifier) throws SQLException {
final Connection connection = getAnyConnection();
//connection.createStatement().execute(String.format("SET SCHEMA \"%s\";", tenantIdentifier));
connection.createStatement().execute("SET Schema 'public'");
return connection;
}
@Override
public void releaseConnection(String tenantIdentifier, Connection connection) throws SQLException {
//connection.createStatement().execute(String.format("SET SCHEMA \"%s\";", TenantIdentifierResolver.DEFAULT_TENANT));
connection.createStatement().execute("SET Schema 'public'");
releaseAnyConnection(connection);
}
@Override
public boolean supportsAggressiveRelease() {
return false;
}
@Override
public boolean isUnwrappableAs(Class unwrapType) {
return false;
}
@Override
public <T> T unwrap(Class<T> unwrapType) {
return null;
}
}
CodePudding user response:
Hibernate expects that property hibernate.multi_tenant_connection_provider
defines either already initialized object or class name which Hibernate can instantiate. In your case TenantConnectionProvider
class has no default constructor, thus Hibernate fails (moreover, you expect that TenantConnectionProvider
is a spring
component). There are typically two options to configure such Hibernate SPIs:
- Hibernate way - interact with spring context using
ManagedBeanRegistry
public class TenantConnectionProvider implements MultiTenantConnectionProvider, ServiceRegistryAwareService {
private ServiceRegistryImplementor serviceRegistry;
// default constructor
public TenantConnectionProvider() {
super();
}
@Override
public void injectServices(ServiceRegistryImplementor serviceRegistry) {
this.serviceRegistry = serviceRegistry;
}
protected DataSource getDataSource() {
return serviceRegistry.getService(ManagedBeanRegistry.class)
.getBean(DataSource.class)
.getBeanInstance();
}
...
}
spring-boot
way - configure JPA properties in Java code:
@Bean
public HibernatePropertiesCustomizer tenantConnectionProviderCustomizer(TenantConnectionProvider tenantConnectionProvider) {
return hibernateProperties -> {
hibernateProperties.put(AvailableSettings.MULTI_TENANT_CONNECTION_PROVIDER, tenantConnectionProvider);
};
}