io.smilego.tenant.persistence.hibernate.DynamicDataSourceBasedMultiTenantConnectionProvider Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of multi-tenant-spring-boot-starter Show documentation
Show all versions of multi-tenant-spring-boot-starter Show documentation
A application used as an example on how to set up pushing
its components to the Central Repository.
package io.smilego.tenant.persistence.hibernate;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.RemovalListener;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import io.smilego.tenant.configuration.ParamConfiguration;
import io.smilego.tenant.model.Param;
import io.smilego.tenant.model.Service;
import io.smilego.tenant.model.Tenant;
import io.smilego.tenant.service.ParamService;
import io.smilego.tenant.service.ServiceService;
import io.smilego.tenant.service.TenantService;
import io.smilego.tenant.util.AESUtils;
import io.smilego.tenant.util.LogBuilder;
import org.hibernate.engine.jdbc.connections.spi.AbstractDataSourceBasedMultiTenantConnectionProviderImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
public class DynamicDataSourceBasedMultiTenantConnectionProvider
extends AbstractDataSourceBasedMultiTenantConnectionProviderImpl {
private static final String TENANT_POOL_NAME_SUFFIX = "DataSource";
protected Logger log = LoggerFactory.getLogger(this.getClass());
@Autowired
@Qualifier("masterDataSource")
private DataSource masterDataSource;
@Autowired
@Qualifier("masterDataSourceProperties")
private DataSourceProperties dataSourceProperties;
@Autowired
@Qualifier("tenantHikariConfig")
private HikariConfig hikariConfig;
@Value("${multitenancy.datasource-cache.maximumSize:100}")
private Long maximumSize;
@Value("${multitenancy.datasource-cache.expireAfterAccess:10}")
private Integer expireAfterAccess;
@Value("${multitenancy.security.encryption-key}")
private String encryptionKey;
@Value("${multitenancy.application.name}")
public String applicationName;
@Autowired
private TenantService tenantService;
@Autowired
private ServiceService serviceService;
@Autowired
private ParamService paramService;
private LoadingCache tenantDataSources;
@PostConstruct
private void createCache() {
tenantDataSources = CacheBuilder.newBuilder()
.maximumSize(maximumSize)
.expireAfterAccess(expireAfterAccess, TimeUnit.MINUTES)
.removalListener((RemovalListener) removal -> {
HikariDataSource ds = (HikariDataSource) removal.getValue();
ds.close(); // tear down properly
log.info(LogBuilder.of()
.header("Closing datasource")
.row("Pool name: ", ds.getPoolName()).build());
})
.build(new CacheLoader() {
public DataSource load(String key) {
setParamsService();
Tenant tenant = tenantService.getTenant(key);
return createAndConfigureDataSource(tenant);
}
});
}
private void setParamsService(){
if (ParamConfiguration.mapParams.isEmpty()){
ParamConfiguration.applicationName = applicationName;
Service service = serviceService.getServiceByName(applicationName);
if(Objects.nonNull(service) && Objects.nonNull(service.getId())){
List params = paramService.getParamsByServiceId(service.getId());
if(Objects.nonNull(params) && params.size()>0){
ParamConfiguration.mapParams.put(service.getName(), params);
}
}
}
}
public void cacheTenants(){
List tenants = tenantService.getAllTenants();
try {
tenants.forEach(f->{
try {
tenantDataSources.get(f.getTenantId());
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
});
} catch (Exception e) {
log.info(LogBuilder.of()
.header("Error getting tenants")
.row("Error: ", e.getMessage()).build());
}
}
private DataSource createAndConfigureDataSource(Tenant tenant) {
HikariConfig config = this.hikariConfig;
config.setUsername(tenant.getDb());
config.setPassword(AESUtils.decrypt(encryptionKey, tenant.getPassword()));
config.setJdbcUrl(tenant.getUrl().concat("/").concat(Tenant.TENANT_DATABASE_PREFIX).concat(applicationName));
config.setPoolName(tenant.getTenantId() + TENANT_POOL_NAME_SUFFIX);
HikariDataSource ds = new HikariDataSource(config);
log.info(LogBuilder.of()
.header("Configured datasource")
.row("Pool name: ", ds.getPoolName()).build());
return ds;
}
@Override
protected DataSource selectAnyDataSource() {
return masterDataSource;
}
@Override
protected DataSource selectDataSource(String tenantIdentifier) {
try {
return tenantDataSources.get(tenantIdentifier);
} catch (ExecutionException e) {
throw new RuntimeException("Failed to load DataSource for tenant: " + tenantIdentifier);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy