我正在使用Spring-Webflux + Spring-data-r2dbc和r2dbc驱动程序来连接到Postgresql数据库的多租户响应应用程序。多租户部分基于架构:每个租户一个架构。因此,根据上下文(例如用户登录)的不同,请求将命中数据库的特定模式。
我正在努力在r2dbc中实现这一目标。理想情况下,它将是Hibernate使用MultiTenantConnectionProvider的方式(请参见示例16.3)。
[我发现并到目前为止所做的事情:
我看着PostgresqlConnectionFactory。有趣的是,在prepareConnection
上有一个电话在setSchema(connection)
:
private Mono<Void> setSchema(PostgresqlConnection connection) {
if (this.configuration.getSchema() == null) {
return Mono.empty();
}
return connection.createStatement(String.format("SET SCHEMA '%s'", this.configuration.getSchema()))
.execute()
.then();
}
也许我需要找到一种方法来重写此方法,以便从上下文而不是配置中动态获取模式?
否则,我可以尝试在请求中将模式指定为表前缀:
String s = "tenant-1";
databaseClient.execute("SELECT * FROM \"" + s + "\".\"city\"")
.as(City.class)
.fetch()
.all()
但是我不能再使用SpringData了,或者我需要重写每个请求以将租户作为参数传递。
任何提示/帮助表示赞赏:)
我也遇到了这个问题。
这是我目前正在做的事情:
作为一个Bean发布PostgresqlConnectionConfigurationBuilder和PostgresqlConnectionFactory:
@Bean
public PostgresqlConnectionConfiguration.Builder postgresqlConnectionConfiguration() {
return PostgresqlConnectionConfiguration.builder()
.host("localhost")
.port(5432)
.applicationName("team-toplist-service")
.database("db")
.username("user")
.password("password");
}
@Bean
@Override
public PostgresqlConnectionFactory connectionFactory() {
return new PostgresqlConnectionFactory(postgresqlConnectionConfiguration()
.build());
}
以便以后可以(在我的业务方法中)使用注入的PostgresqlConnectionConfigurationBuilder实例创建新的PostgresqlConnectionFactory-现在,在构建器上也调用了“ schema”设置器(but)(从传入的[[org.springframework.web.reactive.function.server.ServerRequest中提取租户信息后,我从路由bean传下来。
我的数据库模式遵循appname_tenantId模式,所以我们有一个静态配置为“ app_name”的“ appName”,所以我最终得到了像“ app_name_foo_bar123”这样的架构名称接下来,我们有一个租户标识符,在我的情况下,该标识符将来自一个请求标头,该请求标头必须由位于上游的apache服务器设置(传入请求的X-Tenant-Id标头传递,这样就不必依赖URL做特定于租户的路由)
所以我的“逻辑”当前看起来像这样:
public Flux<TopTeam> getTopTeams(ServerRequest request) {
List<String> tenantHeader = request.headers().header("X-Tenant-Id");
// resolve relevant schema name on the fly
String schema = (appName+ "_" + tenantHeader.iterator().next()).replace("-", "_");
System.out.println("Using schema: " + schema);
// configure connfactory with schema set on the builder
PostgresqlConnectionFactory cf = new PostgresqlConnectionFactory(postgresqlConnectionConfiguration.schema(schema).build());
// init new DatabaseClient with tenant specific connection
DatabaseClient cli = DatabaseClient.create(cf);
return cli
.execute("select * from top_teams ").fetch().all()
.flatMap(map -> {
...
});
});
}
当然可以抽象出这种逻辑,不确定该放在哪里,也许可以将其移到MethodArgumentResolver以便我们可以注入一个已经配置的DatabaseClient