[英]How to connect to multiple MySQL databases as per the header in REST API request
I'm creating a multi tenant spring boot - JPA application. 我正在创建一个多租户Spring Boot-JPA应用程序。
In this application, I want to connect to MySQL Databases using DB name which is sent through API request as header. 在此应用程序中,我想使用通过API请求作为标头发送的数据库名称连接到MySQL数据库。
I checked many multi tenant project samples online but still can't figure out a solution. 我在线检查了许多多租户项目示例,但仍然找不到解决方案。
Can anyone suggest me a way to do this? 有人可以建议我这样做吗?
You can use AbstractRoutingDataSource
to achieve this. 您可以使用
AbstractRoutingDataSource
来实现。 AbstractRoutingDataSource
requires information to know which actual DataSource
to route to(referred to as Context ), which is provided by determineCurrentLookupKey()
method. AbstractRoutingDataSource
需要一些信息来知道要路由到哪个实际DataSource
(称为Context ),该信息由determineCurrentLookupKey()
方法提供。 Using example from here . 从这里使用示例。
Define Context like: 定义上下文,例如:
public enum ClientDatabase {
CLIENT_A, CLIENT_B
}
Then you need to define Context Holder which will be used in determineCurrentLookupKey()
然后,您需要定义Context Holder,该上下文将在
determineCurrentLookupKey()
public class ClientDatabaseContextHolder {
private static ThreadLocal<ClientDatabase> CONTEXT = new ThreadLocal<>();
public static void set(ClientDatabase clientDatabase) {
Assert.notNull(clientDatabase, "clientDatabase cannot be null");
CONTEXT.set(clientDatabase);
}
public static ClientDatabase getClientDatabase() {
return CONTEXT.get();
}
public static void clear() {
CONTEXT.remove();
}
}
Then you can extend AbstractRoutingDataSource
like below: 然后,您可以扩展
AbstractRoutingDataSource
如下所示:
public class ClientDataSourceRouter extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return ClientDatabaseContextHolder.getClientDatabase();
}
}
Finally, DataSource
bean configuration: 最后,
DataSource
bean配置:
@Bean
public DataSource clientDatasource() {
Map<Object, Object> targetDataSources = new HashMap<>();
DataSource clientADatasource = clientADatasource();
DataSource clientBDatasource = clientBDatasource();
targetDataSources.put(ClientDatabase.CLIENT_A,
clientADatasource);
targetDataSources.put(ClientDatabase.CLIENT_B,
clientBDatasource);
ClientDataSourceRouter clientRoutingDatasource
= new ClientDataSourceRouter();
clientRoutingDatasource.setTargetDataSources(targetDataSources);
clientRoutingDatasource.setDefaultTargetDataSource(clientADatasource);
return clientRoutingDatasource;
}
https://github.com/wmeints/spring-multi-tenant-demo https://github.com/wmeints/spring-multi-tenant-demo
Following this logic, I can solve it now. 按照这个逻辑,我现在可以解决它。 Some of the versions need to be upgraded and the codes as well.
一些版本需要升级,代码也需要升级。
Spring Boot version have changed. Spring Boot版本已更改。
org.springframework.boot spring-boot-starter-parent 2.1.0.RELEASE org.springframework.boot spring-boot-starter-parent 2.1.0.RELEASE
Mysql version has been removed. Mysql版本已被删除。
And some small changed in MultitenantConfiguration.java
在
MultitenantConfiguration.java
一些小的更改
@Configuration public class MultitenantConfiguration { @Autowired private DataSourceProperties properties; /** * Defines the data source for the application * @return */ @Bean @ConfigurationProperties( prefix = "spring.datasource" ) public DataSource dataSource() { File[] files = Paths.get("tenants").toFile().listFiles(); Map<Object,Object> resolvedDataSources = new HashMap<>(); if(files != null) { for (File propertyFile : files) { Properties tenantProperties = new Properties(); DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create(this.getClass().getClassLoader()); try { tenantProperties.load(new FileInputStream(propertyFile)); String tenantId = tenantProperties.getProperty("name"); dataSourceBuilder.driverClassName(properties.getDriverClassName()) .url(tenantProperties.getProperty("datasource.url")) .username(tenantProperties.getProperty("datasource.username")) .password(tenantProperties.getProperty("datasource.password")); if (properties.getType() != null) { dataSourceBuilder.type(properties.getType()); } resolvedDataSources.put(tenantId, dataSourceBuilder.build()); } catch (IOException e) { e.printStackTrace(); return null; } } } // Create the final multi-tenant source. // It needs a default database to connect to. // Make sure that the default database is actually an empty tenant database. // Don't use that for a regular tenant if you want things to be safe! MultitenantDataSource dataSource = new MultitenantDataSource(); dataSource.setDefaultTargetDataSource(defaultDataSource()); dataSource.setTargetDataSources(resolvedDataSources); // Call this to finalize the initialization of the data source. dataSource.afterPropertiesSet(); return dataSource; } /** * Creates the default data source for the application * @return */ private DataSource defaultDataSource() { DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create(this.getClass().getClassLoader()) .driverClassName(properties.getDriverClassName()) .url(properties.getUrl()) .username(properties.getUsername()) .password(properties.getPassword()); if(properties.getType() != null) { dataSourceBuilder.type(properties.getType()); } return dataSourceBuilder.build(); }
} }
This change is here due to the DataSourceBuilder
has been moved to another path and its constructor has been changed. 发生此更改是由于
DataSourceBuilder
已移至另一个路径,并且其构造函数已更改。
Also changed the MySQL driver class name in application.properties
like this 还像这样在
application.properties
更改了MySQL驱动程序类名称
spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.driver类名= com.mysql.jdbc.Driver
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.