当前位置:网站首页>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);
}
}
}
边栏推荐
- Homekit supports the matter protocol. What does this imply?
- The two 985 universities share the same president! School: true
- 美国的国家公园概览
- How long is the financial product? Is it better for novices to buy long-term or short-term?
- PHP handwriting a perfect daemon
- R语言dplyr包arrange函数排序dataframe数据、通过多个数据列排序dataframe数据(默认是升序排序)
- 同花顺网上开户安全吗,需要注意什么
- #yyds干货盘点# 解决剑指offer: 判断是不是平衡二叉树
- Service stability governance
- 支持HomeKit、NFC:智汀智能门锁SL1仅需要149元
猜你喜欢

Part C - value types and reference types

How to enable the SMS function of alicloud for crmeb knowledge payment

2-optical-2-electric cascaded optical fiber transceiver Gigabit 2-optical-2-electric optical fiber transceiver Mini embedded industrial mine intrinsic safety optical fiber transceiver

After the uncommitted transactions in the redo log buffer of MySQL InnoDB are persisted to the redo log, what happens if the transaction rollback occurs?

Esp32-c3 introductory tutorial problem ⑦ - fatal error: ESP_ Bt.h: no such file or directory ESP not found_ bt.h

怎么手写vite插件

在線文本過濾小於指定長度工具

Based on your work experience, talk about the quality system construction in software testing
![[website architecture] the unique skill of 10-year database design, practical design steps and specifications](/img/f2/061fa6dd42e57a121401e4f0cf1865.png)
[website architecture] the unique skill of 10-year database design, practical design steps and specifications

What if the test time is not enough?
随机推荐
#云原生征文#深入了解Ingress
The two 985 universities share the same president! School: true
4E1 PDH optical transceiver 19 inch rack type single fiber transmission 20km E1 interface optical network optical transceiver
Follow the promotional music MV of domestic tour in Thailand and travel to Bangkok like local people
R language dplyr package mutate_ The all function multiplies all numeric columns (variables) in the dataframe by a fixed value to generate a new data column, and specifies a user-defined suffix name f
Unity learning day14 -- collaboration and WWW
When did the redo log under InnoDB in mysql start to perform check point disk dropping?
Meta said that the UK security law will "scan all private information", which risks infringing on users' privacy
R language uses the multinom function of NNET package to build a disordered multi classification logistic regression model, uses regression coefficients and their standard errors to calculate the valu
POW共识机制
R language dplyr package arrange function sorts dataframe data and sorts dataframe data through multiple data columns (ascending sort by default)
windows 安装 MySQL
C # learning (advanced course) day14 - features
MySQL使用ReplicationConnection导致的连接失效分析与解决
Have you ever encountered incompatibility between flink1.15.0 and Flink CDC MySQL 2.2.1? f
How can testers get started quickly when they change jobs to a new company?
华三交换机配置SSH远程登录
If there is a problem with minority browsers, do you need to do a compatibility test?
TUIKit 音视频低代码解决方案导航页
Network foundation and framework