Spring Boot 2.3.1 在多租户环境中动态更新 Jdbc 模板架构

Springboot 2.3.1 dynamically update Jdbc template's schema in Multi-tenant environment

我的项目在 spring-boot-starter-parent - "1.5.9.RELEASE" 上,我正在将它迁移到 spring-boot-starter-parent - " 2.3.1.RELEASE".

这是一个多租户环境应用程序,其中一个数据库将有多个模式,并且根据租户ID,在模式之间切换执行。

我已经使用 SimpleNativeJdbcExtractor 实现了这种模式切换,但是在最新的 Springboot 版本中 NativeJdbcExtractor 不再可用。

现有实现的代码片段:

 @Bean
@Scope(
        value = ConfigurableBeanFactory.SCOPE_PROTOTYPE,
        proxyMode = ScopedProxyMode.TARGET_CLASS)
public JdbcTemplate jdbcTemplate() {
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    SimpleNativeJdbcExtractor simpleNativeJdbcExtractor = new SimpleNativeJdbcExtractor() {
        @Override
        public Connection getNativeConnection(Connection con) throws SQLException {
            LOGGER.debug("Set schema for getNativeConnection "+Utilities.getTenantId());
            con.setSchema(Utilities.getTenantId());
            return super.getNativeConnection(con);
        }

        @Override
        public Connection getNativeConnectionFromStatement(Statement stmt) throws SQLException {
            LOGGER.debug("Set schema for getNativeConnectionFromStatement "+Utilities.getTenantId());
            Connection nativeConnectionFromStatement = super.getNativeConnectionFromStatement(stmt);
            nativeConnectionFromStatement.setSchema(Utilities.getTenantId());
            return nativeConnectionFromStatement;
        }
    };

    simpleNativeJdbcExtractor.setNativeConnectionNecessaryForNativeStatements(true);
    simpleNativeJdbcExtractor.setNativeConnectionNecessaryForNativePreparedStatements(true);

    jdbcTemplate.setNativeJdbcExtractor(simpleNativeJdbcExtractor);
    return jdbcTemplate;
}

此处Utilities.getTenantId()(ThreadLocal 中的存储值)将根据 REST 请求给出模式名称。

问题:

非常感谢解决此问题的任何帮助、代码片段或指导。

谢谢。

没有必要摆脱 JdbcTemplateNativeJdbcExtractorremoved in Spring Framework 5 因为 JDBC 不需要它 4.

您应该将 NativeJdbcExtractor 的用法替换为对 connection.unwrap(Class) 的调用。该方法由Connection继承自JDBC的Wrapper

您可能还想考虑使用 AbstractRoutingDataSource,它旨在根据查找键将连接请求路由到不同的基础数据源。

当我 运行 处于调试模式的应用程序时,我看到 Spring 正在选择 Hikari 数据源。

我不得不拦截 getConnection 调用并更新架构。

所以我做了类似下面的事情,

创建了一个扩展 HikariDataSource

的自定义 class
public class CustomHikariDataSource extends HikariDataSource {
@Override
public Connection getConnection() throws SQLException {

    Connection connection =  super.getConnection();
    connection.setSchema(Utilities.getTenantId());
    return connection;
}
}

然后在配置 class 中,我为我的 CustomHikariDataSource class 创建了 bean。

 @Bean
public DataSource customDataSource(DataSourceProperties properties) {

    final CustomHikariDataSource dataSource = (CustomHikariDataSource) properties
            .initializeDataSourceBuilder().type(CustomHikariDataSource.class).build();
    if (properties.getName() != null) {
        dataSource.setPoolName(properties.getName());
    }
    return dataSource;
}

JdbcTemplate bean 将使用它。

 @Bean
@Scope(
        value = ConfigurableBeanFactory.SCOPE_PROTOTYPE,
        proxyMode = ScopedProxyMode.TARGET_CLASS)
public JdbcTemplate jdbcTemplate() throws SQLException {
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    return jdbcTemplate;
}

通过这种方法,我将只创建一次 DataSource bean,并且对于每次 JdbcTemplate 访问,将在运行时更新正确的架构。