1

I have the following issue that I have to solve in my business. I am using Spring for project development I have 8 dataSource to connect.

A request will be made informing the contract number, through this contact number I will have to select one of the 8 dataSource and make the client consultation.

Example: I have the base Brazil = 1,Spain = 2 and Germany = 3

  1. If the contract was id = 1, then you should get the customer data from Brazil base.
  2. If the contract was id = 2, then you should get the customer data from Spain base.
  3. If the contract was id = 3, then you should fetch customer data from the Germany base.

I don't know how to solve this problem if I use multitenancy or AbstractRouting. And I don't know how to start the code for this.

Would anyone have any solution and an example?

2
  • 1
    Did you consider to solve this at infrastructure level? For example you extract functionality that obtains customer data into a separate microservice that is configured to work with single dataSource. Then you set up 8 (or 500) instances of that microservice. With this approach you can just perform a call to relevant node based on given contract ID. Orchestrating all these stuff is not a big deal when Kubernetes goes into battle. Commented Aug 2, 2019 at 9:19
  • This would also be my solution of choice. No need for pesky ThreadLocals or custom coding, ability to add/remove tenants without affecting the other ones, ability to customise the service for a specific tenant should the need ever arise... Commented Aug 2, 2019 at 9:32

2 Answers 2

1

Spring has way to determine datasource dynamically using AbstractRoutingDataSource. But need to use ThreadLocal to bind context to current thread.

This may complicate things if you are spawning multiple threads or using async in your app.

You can refer simple exmaple here

If you really need to create database connection as per different clients then hibernate provides a way to manage Multi-tenancy, Hibernate Guide. As explained general approach would be to use connection pool per-tenant or single connection pool per all tenants sharing same database but different schema.

Below are the two implementation used to switch to multiple tenants -

    public class TestDataSourceBasedMultiTenantConnectionProviderImpl
extends AbstractDataSourceBasedMultiTenantConnectionProviderImpl {

    private static final long serialVersionUID = 14535345L;

    @Autowired
    private DataSource defaultDataSource;

    @Autowired
    private TestDataSourceLookup dataSourceLookup;

    /**
     * 
     * Select datasources in situations where not tenantId is used (e.g. startup
     * processing).
     * 
     */
    @Override
    protected DataSource selectAnyDataSource() {
        //logger.trace("Select any dataSource: " + defaultDataSource);
        return defaultDataSource;
    }

    /**
     * 
     * Obtains a DataSource based on tenantId
     * 
     */

    @Override
    protected DataSource selectDataSource(String tenantIdentifier) {

        DataSource ds = dataSourceLookup.getDataSource(tenantIdentifier);
    //  logger.trace("Select dataSource from " + tenantIdentifier + ": " + ds);
        return ds;

    }

import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
import org.springframework.beans.factory.annotation.Autowired;

public class TestCurrentTenantIdentifierResolverImpl implements CurrentTenantIdentifierResolver  {

    @Autowired
    private RequestContext context;

    @Override
    public String resolveCurrentTenantIdentifier() {
        // TODO Auto-generated method stub
        return context.getTenantID();
    }

    @Override
    public boolean validateExistingCurrentSessions() {
        // TODO Auto-generated method stub
        return true;
    }

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

4 Comments

@Amit This would totally work for a defined number of datasources. 8 is a fair number to have defined in a properties file. But say I have 500 databases, can you suggest a way to dynamically provide connection parameters for the datasources? As in provide connection parameters at runtime as opposed to defining them in a properties file
@jaletechs what is purpose of reading from these many sources? is it going to transcriptional processing? For Analytical processing you should explore OLAP databases something like Redshift
@AmitNaik actually, the system is a Java EE multitenant system. We just have 500 clients each with their own database (mysql, same tables and general structure, but obviously different data) and the J2EE application uses the right database depending on the client. The Spring Boot app I want to build is supposed to work with these databases that already exist, I simply want to perform an operation on a particular database depending on the selected client. This is possible, but J2EE is overkill for a really simple app like this. Just wondering how I could do that with Spring Boot.
@jaletechs I have updated my answer. You can find ample of examples online. hope this helps.
0

AbstractRouting would be a way to do it, indeed. You can mix that approach with headers on the request to easily implement multi-tenancy. The link provided in another post on Baeldung does indeed provide a solution using such construct.

Sharding and deploying a separate micro-service for each tenant, with a routing service in front, would be another one and I think it offers several benefits both from coding (no need for thread-locals or other similarly difficult construct) and maintenance perspective (you can take down your service for maintenance/customisation at tenant level), so it would be my solution of choice in your specific case, assuming that business requirements allow for it.

Still, again, the best solution depends on your specific case.

The easiest solution matching the title of the question - for readers that are not necessarily concerned with multi-tenancy - would be to create several dataSource instances in your Spring's @Configuration beans, put them in a Map<Integer, DataSource> and then return that Map<Integer, DataSource> object as a Bean. When you have to access them, you access the Map bean you created and then create your NamedParameterJdbcTemplate or similar resource passing the specific DataSource in the constructor.

Pseudo-code example:

@Configuration
class DataSourceConfig {
    public final static int SPAIN = 2;
    public final static int BRAZIL = 1;

    @Bean
    @Qualifier("dataSources")
    public Map<Integer, DataSource> dataSources() {
        Map<Integer, DataSource> ds = new HashMap<>();
        ds.put(SPAIN, buildSpainDataSource());
        ds.put(BRAZIL, buildBrazilDataSource());
        return ds;
    }

    private DataSource buildSpainDataSource() {
        ...
    }

    private DataSource buildBrazilDataSource() {
        ...
    }

}

@Service
class MyService {
    @Autowired
    @Qualifier("dataSources")
    Map<Integer, DataSource> dataSources;

    Map<String, Object> getObjectForCountry(int country) {
        NamedParameterJdbcTemplate t = new NamedParameterJdbcTemplate(dataSources.get(country));
        return t.queryForMap("select value from mytable", new HashMap<>());
    }
}

Comments

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.