当前位置:网站首页>Quarkus+saas multi tenant dynamic data source switching is simple and perfect
Quarkus+saas multi tenant dynamic data source switching is simple and perfect
2022-06-23 13:28:00 【InfoQ】

package com.weir.quarku.tanent;
import io.quarkus.arc.Unremovable;
import io.quarkus.hibernate.orm.runtime.tenant.TenantResolver;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import java.util.Optional;
/**
* Identify dynamic tenant information ( Usually by web Of request from header Transfer tenant information )
* @author weir
*
*/
@RequestScoped
@Unremovable
public class InjectableTenantResolver implements TenantResolver {
@Inject
TenantConnections tenantConnections;
private Optional<String> requestTenant = Optional.empty();
public void setRequestTenant(String requestTenant) {
this.requestTenant = Optional.of(requestTenant);
}
/**
* Default tenant
*/
@Override
public String getDefaultTenantId() {
return tenantConnections.allTenants()
.stream()
.findAny()
.orElseThrow(() -> new RuntimeException("No tenants known at all"));
}
/**
* Current tenants
*/
@Override
public String resolveTenantId() {
return requestTenant.orElseThrow(() -> new RuntimeException("No tenant specified by current request"));
}
}
package com.weir.quarku.tanent;
import io.agroal.api.AgroalDataSource;
import io.agroal.api.configuration.AgroalDataSourceConfiguration;
import io.agroal.api.configuration.supplier.AgroalDataSourceConfigurationSupplier;
import io.agroal.api.security.NamePrincipal;
import io.agroal.api.security.SimplePassword;
import io.quarkus.arc.Unremovable;
import io.quarkus.hibernate.orm.runtime.customized.QuarkusConnectionProvider;
import io.quarkus.hibernate.orm.runtime.tenant.TenantConnectionResolver;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.*;
import static io.agroal.api.configuration.AgroalConnectionPoolConfiguration.ConnectionValidator.defaultValidator;
import static java.time.Duration.ofSeconds;
/**
* Dynamically generate and switch data source connections
* @author weir
*
*/
//@PersistenceUnitExtension
@ApplicationScoped
@Unremovable
public class TenantConnections implements TenantConnectionResolver {
private final Map<String, DBConnectionInfo> dbConnectionInfoMap = new HashMap<>();
// {
// {
// put("weir",new DBConnectionInfo("localhost", 3306, "root", "336393", "quarkus-demo"));
// put("weir-blog",new DBConnectionInfo("localhost", 3306, "root", "336393", "weirblog"));
// }
// };
private final Map<String, ConnectionProvider> cache = new HashMap<>();
private static AgroalDataSourceConfiguration createDataSourceConfiguration(DBConnectionInfo dbConnectionInfo) {
System.out.println("------------------createDataSourceConfiguration--------------" + dbConnectionInfo);
return new AgroalDataSourceConfigurationSupplier()
.dataSourceImplementation(AgroalDataSourceConfiguration.DataSourceImplementation.AGROAL)
.metricsEnabled(false)
.connectionPoolConfiguration(cp -> cp.minSize(0).maxSize(5).initialSize(0)
.connectionValidator(defaultValidator()).acquisitionTimeout(ofSeconds(5))
.leakTimeout(ofSeconds(5)).validationTimeout(ofSeconds(50)).reapTimeout(ofSeconds(500))
.connectionFactoryConfiguration(cf -> cf
.jdbcUrl("jdbc:mysql://" + dbConnectionInfo.getHost() + ":" + dbConnectionInfo.getPort()
+ "/" + dbConnectionInfo.getDb())
.connectionProviderClassName("com.mysql.cj.jdbc.Driver")
// .connectionProviderClassName("org.postgresql.Driver")
.principal(new NamePrincipal(dbConnectionInfo.getUser()))
.credential(new SimplePassword(dbConnectionInfo.getPassword()))))
.get();
}
public Set<String> allTenants() {
getTenant();
System.out.println("---------------TenantConnections--------allTenants-------" + dbConnectionInfoMap.keySet());
return dbConnectionInfoMap.keySet();
}
@Inject
AgroalDataSource defaultDataSource;
private void getTenant() {
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
connection = defaultDataSource.getConnection();
statement = connection.createStatement();
resultSet = statement.executeQuery("select * from SysTenant");
while (resultSet.next()) {
dbConnectionInfoMap.put(resultSet.getString("code"),
new DBConnectionInfo(resultSet.getString("host"), resultSet.getInt("port"),
resultSet.getString("username"), resultSet.getString("password"),
resultSet.getString("dbName")));
// System.out.println("--------------------------"+resultSet.getString("name")+"--"+resultSet.getString("username"));
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public ConnectionProvider resolve(String tenant) {
System.out.println("---------------TenantConnections--------resolve-------" + tenant);
if (!dbConnectionInfoMap.containsKey(tenant)) {
throw new IllegalStateException("Unknown tenantId: " + tenant);
}
if (!cache.containsKey(tenant)) {
try {
DBConnectionInfo dbConnectionInfo = dbConnectionInfoMap.get(tenant);
AgroalDataSource agroalDataSource = AgroalDataSource
.from(createDataSourceConfiguration(dbConnectionInfo));
QuarkusConnectionProvider quarkusConnectionProvider = new QuarkusConnectionProvider(agroalDataSource);
cache.put(tenant, quarkusConnectionProvider);
return quarkusConnectionProvider;
} catch (SQLException ex) {
throw new IllegalStateException("Failed to create a new data source based on the tenantId: " + tenant,
ex);
}
}
return cache.get(tenant);
}
}
package com.weir.quarku.tanent;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.ext.Provider;
import java.io.IOException;
/**
* Intercept web in header And set it to TenantResolver(InjectableTenantResolver)
* @author weir
*
*/
@Provider
@ApplicationScoped
public class TenantRequestFilter implements ContainerRequestFilter {
@Inject
InjectableTenantResolver tenantResolver;
@Override
public void filter(ContainerRequestContext containerRequestContext) throws IOException {
String tenantId = containerRequestContext.getHeaderString("X-tenant");
if (tenantId != null) {
tenantResolver.setRequestTenant(tenantId);
}
}
}
边栏推荐
- sed -i命令怎么使用
- R language uses the polR function of mass package to build an ordered multi classification logistic regression model, and uses the summary function to obtain the summary statistical information of the
- Analysis and solution of connection failure caused by MySQL using replicationconnection
- R语言dplyr包arrange函数排序dataframe数据、通过多个数据列排序dataframe数据(默认是升序排序)
- What should testers do if the requirements need to be changed when the project is half tested?
- R语言将距离矩阵输入给hclust函数进行层次聚类分析,使用cutree函数进行层次聚类簇的划分、参数k指定聚类簇的个数、给每个样本都分配了簇标签
- RestCloud ETL解决shell脚本参数化
- The two 985 universities share the same president! School: true
- Go写文件的权限 WriteFile(filename, data, 0644)?
- OS的常见用法(图片示例)
猜你喜欢

【深入理解TcaplusDB技术】单据受理之事务执行

32-way telephone +2-way Gigabit Ethernet 32-way PCM telephone optical transceiver supports FXO port FXS voice telephone to optical fiber

KDD 2022 | epileptic wave prediction based on hierarchical graph diffusion learning

16 channel HD-SDI optical transceiver multi channel HD-SDI HD video optical transceiver 16 channel 3g-sdi HD audio video optical transceiver

618的省钱技术攻略 来啦 -体验场景 领取10元猫超卡!

LM05丨曾经的VIX(二代产品)

What are the criteria for judging the end of the test?

20000 words + 30 pictures | MySQL log: what is the use of undo log, redo log and binlog?

90%的人都不懂的泛型,泛型的缺陷和应用场景

Technology sharing | wvp+zlmediakit realizes streaming playback of camera gb28181
随机推荐
Go write file permission WriteFile (filename, data, 0644)?
ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.
在线文本实体抽取能力,助力应用解析海量文本数据
4E1 PDH optical transceiver 19 inch rack type single fiber transmission 20km E1 interface optical network optical transceiver
Hanyuan hi tech 1-channel gigabit optical port to 4-channel Gigabit Ethernet electrical port Gigabit 1-optical 4-electric optical fiber transceiver
16 channel HD-SDI optical transceiver multi channel HD-SDI HD video optical transceiver 16 channel 3g-sdi HD audio video optical transceiver
How can testers get started quickly when they change jobs to a new company?
LM05丨曾经的VIX(二代产品)
AAIG看全球6月刊(上)发布|AI人格真的觉醒了吗?NLP哪个细分方向最具社会价值?Get新观点新启发~
The R language inputs the distance matrix to the hclust function for hierarchical clustering analysis, uses the cutree function to divide the hierarchical clustering clusters, specifies the number of
How did Tencent's technology bulls complete the overall cloud launch?
How about stock online account opening and account opening process? Is it safe to open a mobile account?
What are the criteria for judging the end of the test?
.Net怎么使用日志框架NLog
Tuikit audio and video low code solution navigation page
Online text filter less than specified length tool
Hanyuan hi tech 8-way telephone +1-way 100M Ethernet RJ11 telephone optical transceiver 8-way PCM telephone optical transceiver
Hanyuan high tech new generation green energy-saving Ethernet access industrial switch high efficiency energy-saving Gigabit Industrial Ethernet switch
Cloud native essay deep understanding of ingress
Playing in Singapore in the hot summer: an inventory of indoor attractions and good places for night trips