当前位置:网站首页>quarkus+saas多租户动态数据源切换实现简单完美
quarkus+saas多租户动态数据源切换实现简单完美
2022-06-23 11:03: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;
/**
* 识别动态租户信息(一般通过web的request从header传递租户信息)
* @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);
}
/**
* 默认租户
*/
@Override
public String getDefaultTenantId() {
return tenantConnections.allTenants()
.stream()
.findAny()
.orElseThrow(() -> new RuntimeException("No tenants known at all"));
}
/**
* 当前租户
*/
@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;
/**
* 动态产生并切换数据源连接
* @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;
/**
* 拦截web中header的租户信息并设置给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);
}
}
}
边栏推荐
- 韦东山设备信息查询例程学习
- The simplest DIY actuator cluster control program based on 51 single chip microcomputer, pca9685, IIC and PTZ
- Whether Changan Lumin has the ability to become a broken product in the micro electricity market
- R and rstudio download and install detailed steps
- 开源二进制文件静态漏洞分析工具BinAbsInspector安装使用
- Noi OJ 1.2 06: round floating point numbers to zero
- 【ML】QuantileRegressor
- C语言结构体字节对齐问题
- 技术创造价值,手把手教你薅羊毛篇
- 详解判断大小端的方法
猜你喜欢

Whether Changan Lumin has the ability to become a broken product in the micro electricity market

为什么指针做形参没有改变对应的值

Over a year, time has changed. Chinese chips have made breakthroughs, but American chips are difficult to sell

Android security / reverse interview questions

MAUI使用Masa blazor组件库
[Architect (Part 40)] connecting mongodb database developed by server

flutter系列之:flutter中的Wrap

详解判断大小端的方法

最简单DIY基于51单片机的舵机控制器

Simplest DIY remote control computer system based on STM32 ② (wireless remote control + key control)
随机推荐
The simplest DIY pca9685 steering gear control program based on the integration of upper and lower computers of C # and 51 single chip microcomputer
Noi OJ 1.3 09: circle related computing C language
一年多时间时移世易,中国芯片不断突破,美国芯片却难以卖出
最简单DIY基于STM32F407探索者开发板的MPU6050陀螺仪姿态控制舵机程序
力扣 1319. 连通网络的操作次数
Noi OJ 1.4 01: positive and negative C language
torch权重转mindspore
Flush recommended? Is it safe to open a mobile account?
Noi OJ 1.2 06: round floating point numbers to zero
ESP32-CAM高性价比温湿度监控系统
Why does the pointer not change the corresponding value as a formal parameter
攻防演练合集 | 3个阶段,4大要点,蓝队防守全流程纲要解读
Tensorrt筆記(四)推理分割模型
ESP32-CAM无线监控智能网关的设计与实现
最简单DIY基于STM32的远程控制电脑系统②(无线遥杆+按键控制)
Deep dive kotlin synergy (XIV): problems of shared state
Noi OJ 1.3 04: C language with remainder Division
Tensorrt notes (4) Modèle de segmentation du raisonnement
The simplest DIY actuator controller based on 51 single chip microcomputer
实现常用C语言字符串处理函数