Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to use Mongodb Single instance with multiple dbs in spring-boot

I have a situation where I have multiple clients connecting to my application and I want to give each one their own "schema/database" in the same Mongo server.

My configuration class:

@Configuration
public class MongoDbConfiguration {

@Bean
@Primary
public MongoDbFactory mongoDbFactory() throws UnknownHostException {
    return new MultiTenantMongoDbFactory();
}

@Bean
@Primary
public MongoTemplate mongoTemplate() throws UnknownHostException {
    return new MongoTemplate(mongoDbFactory());
}
}

Multitenant Db Factory

public class MultiTenantMongoDbFactory extends SimpleMongoDbFactory {

public MultiTenantMongoDbFactory() throws UnknownHostException {
    super(getMongoClient(), TenantContext.getTenant());
}

@Override
public DB getDb() throws DataAccessException {
    String tenant = TenantContext.getTenant();
    return getDb(tenant);

}

private static MongoClient getMongoClient() {
    String tenant = TenantContext.getTenant();
    System.out.println("Database name in factory class :"+tenant);
    if (tenant.equalsIgnoreCase("ncet")) {
        MongoCredential mongoCredential = MongoCredential.createCredential("user1", "db1",
                "pwd1".toCharArray());
        ServerAddress serverAddress = new ServerAddress("localhost", 27017);
        MongoClient mongoClient = new MongoClient(serverAddress, Arrays.asList(mongoCredential));
        return mongoClient;
    }else{
        MongoCredential mongoCredential = MongoCredential.createCredential("user1", "db2",
                "pwd2".toCharArray());
        ServerAddress serverAddress = new ServerAddress("localhost", 27017);
        MongoClient mongoClient = new MongoClient(serverAddress, Arrays.asList(mongoCredential));
        return mongoClient;
    }

}

Each database has credentials

like image 238
Lakshman Miani Avatar asked Oct 19 '17 10:10

Lakshman Miani


People also ask

How do I connect multiple DBS in spring boot?

To connect multiple databases, each database should be configured in its own spring boot configuration file. Multiple data sources should be configured for multiple databases. For spring data classes and spring data JPA repositories, two separate java packages need be created.

Can we configure 2 DB in spring boot?

Multiple Databases in Spring Boot Spring Boot can simplify the configuration above. Now we have defined the data source properties inside persistence-multiple-db-boot.


1 Answers

Your sample doesn't work because getMongoClient() invokes only once during startup, but you need to change it in runtime based on active tenant. It's pretty straightforward to implement dedicated MongoDbFactory for multi-tenancy based on spring SimpleMongoDbFactory as example. You can add more logic to it if needed (for writeConcern, etc). There are two tenants (east and west) in this sample. Each tenant has its own MongoClient with corresponding database name and credentials configured in MongoConfig. TenantDataFactory returns tenant related information based on current Tenant in TenantContext. DB object is created using MongoClient and database name from TenantData returned by TenantDataFactory.

public class MultiTenantMongoDbFactory implements MongoDbFactory {

  private PersistenceExceptionTranslator exceptionTranslator;
  private TenantDataFactory tenantDataFactory;

  public MultiTenantMongoDbFactory(TenantDataFactory tenantDataFactory) {
    this.exceptionTranslator = new MongoExceptionTranslator();
    this.tenantDataFactory = tenantDataFactory;
  }

  @Override
  public DB getDb(String dbName) throws DataAccessException {
    return getDb();
  }

  @Override
  public DB getDb() throws DataAccessException {
    Tenant tenant = TenantContext.getCurrentTenant();
    TenantData tenantData = tenantDataFactory.getTenantData(tenant);
    return MongoDbUtils.getDB(tenantData.getClient(), tenantData.getDbName());
  }

  @Override
  public PersistenceExceptionTranslator getExceptionTranslator() {
    return exceptionTranslator;
  }
}

public class TenantDataFactory {

  private Map<Tenant, TenantData> tenantDataMap;

  public TenantDataFactory(Map<Tenant, TenantData> tenantDataMap) {
    this.tenantDataMap = Collections.unmodifiableMap(tenantDataMap);
  }

  public TenantData getTenantData(Tenant tenant) {
    TenantData tenantData = tenantDataMap.get(tenant);
    if (tenantData == null) {
      // or return default tenant
      throw new IllegalArgumentException("Unsupported tenant " + tenant);
    }
    return tenantData;
  }
}

public enum Tenant {
  EAST, WEST
}

public class TenantData {

  private final String dbName;
  private final MongoClient client;

  public TenantData(String dbName, MongoClient client) {
    this.dbName = dbName;
    this.client = client;
  }

  public String getDbName() {
    return dbName;
  }

  public MongoClient getClient() {
    return client;
  }
}

public class TenantContext {

  private static ThreadLocal<Tenant> currentTenant = new ThreadLocal<>();

  public static void setCurrentTenant(Tenant tenant) {
    currentTenant.set(tenant);
  }

  public static Tenant getCurrentTenant() {
    return currentTenant.get();
  }
}

@Configuration
public class MongoConfig {

  @Bean(name = "eastMongoClient", destroyMethod = "close")
  public MongoClient eastMongoClient() {
    return new MongoClient(new ServerAddress("127.0.0.1", 27017),
        Collections.singletonList(MongoCredential.createCredential("user1", "east", "password1".toCharArray())));
  }

  @Bean(name = "westMongoClient", destroyMethod = "close")
  public MongoClient westMongoClient() {
    return new MongoClient(new ServerAddress("127.0.0.1", 27017),
        Collections.singletonList(MongoCredential.createCredential("user2", "west", "password2".toCharArray())));
  }

  @Bean
  public TenantDataFactory tenantDataFactory(@Qualifier("eastMongoClient") MongoClient eastMongoClient,
                                             @Qualifier("westMongoClient") MongoClient westMongoClient) {
    Map<Tenant, TenantData> tenantDataMap = new HashMap<>();
    tenantDataMap.put(Tenant.EAST, new TenantData("east", eastMongoClient));
    tenantDataMap.put(Tenant.WEST, new TenantData("west", westMongoClient));
    return new TenantDataFactory(tenantDataMap);
  }

  @Bean
  public MongoDbFactory mongoDbFactory(@Autowired TenantDataFactory tenantDataFactory) {
    return new MultiTenantMongoDbFactory(tenantDataFactory);
  }

  @Bean
  public MongoTemplate mongoTemplate(@Autowired MongoDbFactory mongoDbFactory) {
    return new MongoTemplate(mongoDbFactory);
  }
}
like image 158
Nikita Gorbachevski Avatar answered Oct 13 '22 00:10

Nikita Gorbachevski