0

I am implementing a multi-tenant architecture in my Spring application. The ConfigurationDataSource class is used to provide a DataSource when the application starts. Below is the detailed implementation of the class:

ConfigurationDataSource

import com.example.hikaricp_demo.domain.TenantConfig;
import com.example.hikaricp_demo.repository.TenantConfigRepository;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Configuration
public class ConfigurationDataSource {

    @Autowired
    private TenantConfigRepository repository;

    private List<TenantConfig> getListTenant(){
        return repository.findAll();
    }

    TenantConfig tenantDefault() {
        return repository.findByTenantId("tenantDefault");
    }

    @Bean
    public DataSource dataSource() {
        Map<Object, Object> resolvedDataSources = new HashMap<>();
        List<TenantConfig> listTenant = getListTenant();

        for (TenantConfig item : listTenant) {
            HikariConfig hikariConfig = new HikariConfig();
            hikariConfig.setDriverClassName(item.getDriverClassName());
            hikariConfig.setJdbcUrl(item.getUrl());
            hikariConfig.setUsername(item.getUsername());
            hikariConfig.setPassword(item.getPassword());
            hikariConfig.setSchema(item.getSchema());

            HikariDataSource dataSource = new HikariDataSource(hikariConfig);
            resolvedDataSources.put(item.getTenantId(), dataSource);
        }

        AbstractRoutingDataSource dataSource = new MultiTenantDataSource();
        dataSource.setDefaultTargetDataSource(tenantDefault());
        dataSource.setTargetDataSources(resolvedDataSources);
        dataSource.afterPropertiesSet();
        return dataSource;
    }
}

The TenantConfigRepository is a Spring Data JPA repository that interacts with the database to fetch tenant configurations. When I run the application, I get the following error:

Description:

The dependencies of some of the beans in the application context form a cycle:

   entityManagerFactory defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]
┌─────┐
|  dataSourceScriptDatabaseInitializer defined in class path resource [org/springframework/boot/autoconfigure/sql/init/DataSourceInitializationConfiguration.class]
↑     ↓
|  configurationDataSource (field private com.example.hikaricp_demo.repository.TenantConfigRepository com.example.hikaricp_demo.config.ConfigurationDataSource.repository)
↑     ↓
|  tenantConfigRepository defined in com.example.hikaricp_demo.repository.TenantConfigRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration
↑     ↓
|  jpaSharedEM_entityManagerFactory
└─────┘

I suspect this happens because:

The dataSource bean depends on TenantConfigRepository (to fetch tenant configurations using repository.findAll()).

The TenantConfigRepository might also depend on DataSource, creating a circular dependency.

1
  • Well this is never going to work. As you need a datasource to create the repository (or rather config JPA), but to create the repository you need a datasource. So your current setup will simply never work. Commented Nov 28, 2024 at 7:45

1 Answer 1

-1
@Bean
@Lazy
public DataSource dataSource() {
    //your code
}

Using @Lazy Annotation to Delay Initialization Data Source, Lowering Initialization Priority or Use @Primary annotation for default data source to clearly identify the primary data source

Sign up to request clarification or add additional context in comments.

4 Comments

it might be better if you added some explanation to your answer
Delayed Initialization Data Source: Using @Lazy Annotation to Delay Initialization Data Source, Lowering Initialization Priority
... Probably better if you edited your question and put the information in there.
This won't work as DB access is needed to retrieve the information. So making it lazy will simply not work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.