Skip to content

Multi-Tenancy with Spring boot(2.7.18) + Hibernate: "SessionFactory configured for multi-tenancy, but no tenant identifier specified" #3406

@NaveenRamu

Description

@NaveenRamu

Hi,

I created a multi-tenancy application with Spring Boot and JPA.
When TenantIdentifier is not returning any default tenant value then my application is failing with the below error.

image

` TenantContext.java

public class TenantContext {

private static final InheritableThreadLocal<String> CURRENT_TENANT = new InheritableThreadLocal();
private static final Logger LOGGER = LoggerFactory.getLogger(TenantContext.class);

public TenantContext() {
}

public static String getCurrentTenant() {
    return (String)CURRENT_TENANT.get();
}

public static void setCurrentTenant(String tenant) {
    CURRENT_TENANT.set(tenant);
    LOGGER.debug("Setting current tenant in tenant context to:{}", tenant);
}
}

`

`
TenantIdentifier.java

@Component
public class TenantIdentifier implements CurrentTenantIdentifierResolver {

private static final Logger logger = LoggerFactory.getLogger(TenantIdentifier.class);

@Override
public String resolveCurrentTenantIdentifier() {
    logger.info("resolveCurrentTenantIdentifier called");
    String tenant = TenantContext.getCurrentTenant();
    logger.info("resolveCurrentTenantIdentifier called tenant {}", tenant);
    return tenant;
}

@Override
public boolean validateExistingCurrentSessions() {
    return true;
}
}

`

`PersistenceJpaConfigWithMultitenancy.java

@Configuration
@EnableJpaRepositories(basePackages = {"*****.**.persistence.repository"}, repositoryFactoryBeanClass = 
ConfigurationRepositoryFactory.class)
@EnableTransactionManagement
public class PersistenceJpaConfigWithMultitenancy {

private static final Logger logger = LoggerFactory.getLogger(PersistenceJpaConfigWithMultitenancy.class);

@Autowired
HikariDataSourceBuilder hikariDataSourceBuilder;

@Autowired
private EnvironmentService environmentService;


@Bean
public MultiTenantConnectionProvider multiTenantConnectionProvider() {
    logger.info("Called DataSourceBasedMultiTenantConnectionProviderImpl");
    return new DataSourceBasedMultiTenantConnectionProviderImpl();
}

@Bean
public CurrentTenantIdentifierResolver currentTenantIdentifierResolver() {
    logger.info("called TenantIdentifier");
    return new TenantIdentifier();
}

private LocalContainerEntityManagerFactoryBean getLocalContainerEntityManagerFactoryBean() {
    return new LocalContainerEntityManagerFactoryBean();
}

@Bean(name = "entityManagerFactory")
@Primary
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() {
    logger.info("Setting the entityManagerFactory bean"); //TODO: to be removed later
    JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
    Map<String, Object> hibernateProps = new LinkedHashMap<>();
    hibernateProps.put(Environment.MULTI_TENANT, MultiTenancyStrategy.DATABASE);
    hibernateProps.put(Environment.MULTI_TENANT_CONNECTION_PROVIDER, 
    DataSourceBasedMultiTenantConnectionProviderImpl.class);
    hibernateProps.put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, TenantIdentifier.class);

    LocalContainerEntityManagerFactoryBean em = getLocalContainerEntityManagerFactoryBean();

    em.setPackagesToScan("com.swgrp.itomdi.administration.persistence.model");
    em.setJpaVendorAdapter(vendorAdapter);
    em.setJpaProperties(additionalProperties());
    em.setJpaPropertyMap(hibernateProps);

    return em;
}

@Bean
@ConditionalOnProperty(
        name = "config.store.type",
        havingValue = "db")
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
    JpaTransactionManager transactionManager
            = new JpaTransactionManager();
    transactionManager.setEntityManagerFactory(entityManagerFactory);
    return transactionManager;
}

@Bean
public EntityManagerFactory entityManagerFactory(LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) {
    return entityManagerFactoryBean.getObject();
}

@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
    return new PersistenceExceptionTranslationPostProcessor();
}

Properties additionalProperties() {
    Properties properties = new Properties();
    properties.setProperty("spring.jpa.database", "Vertica");
    properties.setProperty("hibernate.show_sql", "true");
    properties.setProperty(
            "hibernate.dialect", "org.hibernate.dialect.VerticaDialect");
    properties.setProperty("spring.data.jpa.repositories.enabled", "true");
    properties.setProperty("hibernate.temp.use_jdbc_metadata_defaults", "false");
    return properties;
}
}

`

` DataSourceBasedMultiTenantConnectionProviderImpl.java

@Component
public class DataSourceBasedMultiTenantConnectionProviderImpl extends 
AbstractDataSourceBasedMultiTenantConnectionProviderImpl {

private static Logger logger = LoggerFactory.getLogger(DataSourceBasedMultiTenantConnectionProviderImpl.class);

@Autowired
private HikariDataSourceBuilder hikariDataSourceBuilder;

@Autowired
private EnvironmentService environmentService;


@Override
protected DataSource selectAnyDataSource() {
    logger.info("Calling selectAnyDataSource");
    return AdministrationCacheProvider.getAdminCache().values().iterator().next().getTenantDataSource(); //TODO: check if this can be removed and send null instead
}

@Override
protected DataSource selectDataSource(String tenant) {
    logger.info("Returning datasource for tenant:{}", tenant);
    return AdministrationCacheProvider.getAdminCache().get(tenant).getTenantDataSource();
}
}

`

Metadata

Metadata

Labels

status: declinedA suggestion or change that we don't feel we should currently apply

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions