在hibernate中一共有三种方式时间多租户场景
1.使用@TenantId 注解 (非JPA标准注解)
@Entity
public class Account {
@Id @GeneratedValue Long id;
@TenantId String tenantId;
...
}
在使用 @TenantId注解后在使用CriteriaQuery API查询 是会自动带上租户ID
2.使用多Schema
1.需要实现 CurrentTenantIdentifierResolver 获取当前系统的租户ID
/**
* @author Sun
* JPA 多租户配置
*/
@Component
public class UnitMBCurrentTenantIdentifierResolver implements CurrentTenantIdentifierResolver {
/**
* 获取租户Id
*
* @return String
*/
@Override
public String resolveCurrentTenantIdentifier() {
return UnitMBContextHolder.getTenantId();
}
@Override
public boolean validateExistingCurrentSessions() {
return true;
}
}
2.需要继承AbstractMultiTenantConnectionProvider通过租户ID获取对应的Schema链接
public class ConfigurableMultiTenantConnectionProvider
extends AbstractMultiTenantConnectionProvider<String> {
private final Map<String, ConnectionProvider> connectionProviderMap =
new HashMap<>();
public ConfigurableMultiTenantConnectionProvider(
Map<String, ConnectionProvider> connectionProviderMap) {
this.connectionProviderMap.putAll(connectionProviderMap);
}
@Override
protected ConnectionProvider getAnyConnectionProvider() {
return connectionProviderMap.values().iterator().next();
}
@Override
protected ConnectionProvider selectConnectionProvider(String tenantIdentifier) {
return connectionProviderMap.get(tenantIdentifier);
}
}
3.使用多数据源
1.需要实现 CurrentTenantIdentifierResolver 获取当前系统的租户ID
/**
* @author Sun
* JPA 多租户配置
*/
@Component
public class UnitMBCurrentTenantIdentifierResolver implements CurrentTenantIdentifierResolver {
/**
* 获取租户Id
*
* @return String
*/
@Override
public String resolveCurrentTenantIdentifier() {
return UnitMBContextHolder.getTenantId();
}
@Override
public boolean validateExistingCurrentSessions() {
return true;
}
}
2.继承 AbstractDataSourceBasedMultiTenantConnectionProviderImpl 同过租户ID获取对应数据源
/**
* @author Sun
* JPA 多租户配置
*/
@Component
public class UnitMBMultiTenantConnectionProvider extends AbstractDataSourceBasedMultiTenantConnectionProviderImpl implements InitializingBean {
@Serial
private static final long serialVersionUID = 2613286984727472722L;
private static final Logger log = LoggerFactory.getLogger(UnitMBMultiTenantConnectionProvider.class);
@Value("${spring.profiles.active}")
private String active;
/**
* 数据源
*/
private static final Map<String, DataSource> DATA_SOURCE_MAP = new ConcurrentHashMap<>();
/**
* 获取默认租户
*
* @return DataSource
*/
@Override
protected DataSource selectAnyDataSource() {
return DATA_SOURCE_MAP.values().iterator().next();
}
/**
* 获取租住数据数据源
*
* @param tenant 租户
* @return DataSource
*/
@Override
protected DataSource selectDataSource(String tenant) {
String value = new String(Base64.getDecoder().decode(tenant), StandardCharsets.UTF_8);
log.info("租户数据源:{}", value);
DataSource dataSource = DATA_SOURCE_MAP.get(tenant);
if (dataSource == null) {
String[] arr = value.split("-");
dataSource = this.createDataSource(arr[0], arr[1]);
DATA_SOURCE_MAP.put(tenant, dataSource);
}
return dataSource;
}
/**
* 创建数据源
*
* @param address 数据源地址
* @param dbIndex 数据库索引
* @return DataSource
*/
private DataSource createDataSource(String address, String dbIndex) {
String jdbcUrl = "jdbc:mysql://" + address + ":3306/" + UWMSEnvironment.DATABASE_PREFIX + "-" + dbIndex;
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl(jdbcUrl);
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUsername("root");
dataSource.setPassword("123456");
dataSource.setMinimumIdle(5);
dataSource.setMaximumPoolSize(10);
dataSource.setValidationTimeout(3000);
dataSource.setMaxLifetime(60000);
return dataSource;
}
/**
* @throws Exception
*/
@Override
public void afterPropertiesSet() throws Exception {
String address = "pro".equals(active) ? "172.26.190.124" : "192.168.1.100";
String dbIndex = "0";
String tenant = Base64.getEncoder().encodeToString(String.join("-", address, dbIndex).getBytes(StandardCharsets.UTF_8));
DATA_SOURCE_MAP.put(tenant, this.createDataSource(address, dbIndex));
}
}