Я разрабатываю мультитенантное приложение (отдельная база данных для каждого арендатора), используя Spring-Boot и MySQL для базы данных. Вот несколько важных моментов в моем заявлении:
public class TenantRoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return TenantContext.getCurrentTenant() != null ? TenantContext.getCurrentTenant() : "master";
}
}
Это мой конфигурационный компонент DataSource.
@Configuration
public class DataSourceConfig {
@Bean
public DataSource dataSource() {
TenantRoutingDataSource routingDataSource = new TenantRoutingDataSource();
Map<Object, Object> dataSourceMap = new HashMap<>();
DriverManagerDataSource masterDataSource=new DriverManagerDataSource();
masterDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
masterDataSource.setUrl("jdbc:mysql://localhost:3306/test3");
masterDataSource.setUsername("root");
masterDataSource.setPassword("your_new_password");
dataSourceMap.put("master", masterDataSource);
// Populate the map with DataSource for each tenant
try{
List<String> tenantNames = getTenantDatabases(masterDataSource);
tenantNames.forEach((tenant) -> {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/" + tenant);
dataSource.setUsername("root");
dataSource.setPassword("password");
dataSourceMap.put(tenant, dataSource);
});
routingDataSource.setTargetDataSources(dataSourceMap);
}
catch(Exception e)
{
System.out.println(e);
}
return routingDataSource;
}
public List<String> getTenantDatabases(DataSource dataSource) throws Exception {
List<String> tenantDatabases = new ArrayList<>();
try (Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT name FROM tenant_info")) {
while (resultSet.next()) {
tenantDatabases.add(resultSet.getString("name"));
}
}
return tenantDatabases;
}
}
ПРОБЛЕМА Моя проблема заключается в том, что компонент источника данных настраивается во время запуска приложения, и когда новый арендатор регистрируется во время выполнения, он не добавляется в настроенный источник данных. Мне нужно перезапустить приложение, чтобы это произошло. Есть ли решение, позволяющее добавить новый источник данных в существующий список источников данных?
Что я думал попробовать
Если у вас есть другая стратегия, предложите ее.




Ответ на свой вопрос нашла здесь. Также обратитесь к этому репозиторию
@Slf4j
@Configuration
public class MultiTenantManager {
private final Map<Object, Object> tenantDataSources = new ConcurrentHashMap<>();
private final DataSourceProperties properties;
private Function<String, DataSourceProperties> tenantResolver;
private AbstractRoutingDataSource multiTenantDataSource;
public MultiTenantManager(DataSourceProperties properties) {
this.properties = properties;
}
@Bean
public DataSource dataSource() {
multiTenantDataSource = new AbstractRoutingDataSource() {
@Override
protected Object determineCurrentLookupKey() {
return currentTenant.get();
}
};
multiTenantDataSource.setTargetDataSources(tenantDataSources);
multiTenantDataSource.setDefaultTargetDataSource(defaultDataSource());
multiTenantDataSource.afterPropertiesSet();
return multiTenantDataSource;
}
public void addTenant(String tenantId, String url, String username, String password) throws SQLException {
DataSource dataSource = DataSourceBuilder.create()
.driverClassName(properties.getDriverClassName())
.url(url)
.username(username)
.password(password)
.build();
// Check that new connection is 'live'. If not - throw exception
try(Connection c = dataSource.getConnection()) {
tenantDataSources.put(tenantId, dataSource);
multiTenantDataSource.afterPropertiesSet();
log.debug("[d] Tenant '{}' added.", tenantId);
}
}
}
В методе addTenat после добавления нового источника данных
multitenantDatasource.afterPropertiesSet()
метод вызывается для проверки общей конфигурации bean-компонентов и окончательной инициализации. Используя эту настройку, вы можете динамически добавлять новые источники данных.
Спасибо @Cepr0
@PeterJ Я отредактировал ответ, чтобы было ясно. Спасибо за предложение.