当前位置:网站首页>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);
}
}
}
边栏推荐
- 最简单DIY基于51单片机的舵机控制器
- Groovy之范围
- [use of Matlab graphical user interface controls]
- Win10 wireless network. If the system cannot search WLAN, the solution (and VMnet1, 8)
- Noi OJ 1.2 integer data type storage space size
- Is the online security of securities account opening high
- Mysql-03. Experience of SQL optimization in work
- Noi OJ 1.3 05: floating point numeric C language for calculating fractions
- Deep dive kotlin synergy (XIV): problems of shared state
- "Internet +" contest topic hot docking | I figure to understand 38 propositions of Baidu
猜你喜欢

Android security / reverse interview questions

攻防演练合集 | 3个阶段,4大要点,蓝队防守全流程纲要解读

Rancher 2.6 全新 Monitoring 快速入门

最简单DIY基于蓝牙、51单片机和舵机的钢铁爱国者机关枪控制器

圖片存儲--引用

最简单DIY基于C#和51单片机上下位机一体化的PCA9685舵机控制程序

社招腾讯高P(高级产品经理)的面试手册

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

New technology aesthetics and original biological networking operating system reshape the whole house intelligence

Simplest DIY steel patriot machine gun controller based on Bluetooth, 51 MCU and steering gear
随机推荐
"Core" has spirit "lizard", ten thousand people are online! Dragon Dragon community walks into Intel meetup wonderful review
不止于观测|阿里云可观测套件正式发布
The problem of C language structure byte alignment
分享一个手游脚本源码
开发增效利器—2022年VsCode插件分享
5 login failures, limiting login practice
After repeated pressure, Apple may significantly increase the price of iphone14
一年多时间时移世易,中国芯片不断突破,美国芯片却难以卖出
What are the top ten securities companies in China? Is it safe to open a mobile account?
Noi OJ 1.3 11: C language for calculating the remainder of the division of floating-point numbers
Mysql-03. Experience of SQL optimization in work
Win10 Microsoft input method (Microsoft Pinyin) does not display the word selection column (unable to select words) solution
The simplest DIY actuator cluster control program based on 51 single chip microcomputer, pca9685, IIC and PTZ
Noi OJ 1.3 04: C language with remainder Division
Design and implementation of distribution network and Internet connection scheme for esp32-cam high cost performance temperature and humidity monitoring system
Tensorrt笔记(四)推理分割模型
Share a mobile game script source code
Whether Changan Lumin has the ability to become a broken product in the micro electricity market
list的深度剖析及模拟实现
Noi OJ 1.2 integer data type storage space size