繁体   English   中英

在基于 Spring Boot 的应用程序中使用 REST API 输入在运行时设置模式名称

[英]Set schema name at run time using REST API input in spring boot based application

postgres数据库中,模式名称被设置为买家 ID (因此,如果买家 ID 是buyer_2213,那么模式名称将是buyer_2213 )。 这些模式有多个表,并且这些表具有所有模式的通用结构。

现在,我正在使用 Spring Boot 编写 REST API 以从这些架构中获取买家数据,但由于架构名称依赖于买家 ID ,因此我必须编写手动查询并且无法为此使用 JPA 功能。

有什么方法可以使用 REST API 请求参数将架构名称设置为实体。 因此,在下面的实体中,我们可以使用在buyerController 中定义的API 调用中传递的buyerId来设置模式

  @Entity
  @Table(name="buyer_table", schema="set_it_using_API_input")
  public class BuyerTable{
    ...
  }


  @RestController
  @RequestMapping("/buyer")
  public class BuyerController{
    
    @GetMapping("/{buyerId}")
    public void getBuyerData(@PathVariable(required = true) String buyerId){
      ...
    }
  }

此外,买家 ID 与登录用户不同(将此情况视为尝试获取买家详细信息的管理员用户),并且将仅作为 API 请求参数提供(或作为 API 输入的任何其他方式)。 因此我找不到与相关的内容

我终于找到了一个可行的解决方案。 此解决方案主要使用此处的配置,但更具体地针对我的问题要求。

这个想法显然是使用AbstractDataSource并且数据源配置与此处显示的非常相似,只是模式名称将使用可以从 API 逻辑内部调用的setter 设置
首先,我们需要编写一个AbstractDataSource的实现,它看起来像这样:

public class BuyerSchemaDataSource extends AbstractDataSource {

    private LoadingCache<String, DataSource> dataSources = createCache();

    public void setSchemaName(String schemaName){
        this.schemaName = schemaName;
    }

    @Override public Connection getConnection() throws SQLException {
        try {
            return determineTargetDataSource().getConnection();
        } catch (ExecutionException e) {
            //print exception
            return null;
        }
    }

    @Override public Connection getConnection(String username, String password)
        throws SQLException {
        try {
            return determineTargetDataSource().getConnection(username,password);
        } catch (ExecutionException e) {
            //print exception
            return null;
        }
    }

    private DataSource determineTargetDataSource() throws ExecutionException {
        if(!utils.isNullOrEmpty(schemaName)){
            return dataSources.get(schemaName);
        }
        return buildDataSourceFromSchema(null);
    }

    private LoadingCache<String, DataSource> createCache(){
        return CacheBuilder.newBuilder()
            .maximumSize(100)
            .expireAfterWrite(10, TimeUnit.MINUTES)
            .build(new CacheLoader<String, DataSource>() {
                @Override public DataSource load(String key) throws Exception {
                    return buildDataSourceFromSchema(key);
                }
            });
    }

    private DataSource buildDataSourceForSchema(String schema) {
        // e.g. of property: "jdbc:postgresql://localhost:5432/mydatabase?currentSchema="
        String url = env.getRequiredProperty("spring.datasource.url") + schema;
        return DataSourceBuilder.create()
            .driverClassName(env.getRequiredProperty("spring.datasource.driverClassName"))
            [...]
            .url(url)
            .build();
    }
}


现在,就像任何其他数据源一样,它可以在 spring 配置文件中使用,如下所示:
 @Configuration @EnableTransactionManagement @EnableJpaRepositories(entityManagerFactoryRef = "schemaSpecificEntityManagerFactory", transactionManagerRef = "schemaSpecificTransactionManager") public class SchemaSpecificConfig { @Bean(name = "schemaSpecificDataSource") public DataSource schemaSpecificDataSource(){ return new BuyerSchemaDataSource(); } @Bean(name = "schemaSpecificEntityManagerFactory") public LocalContainerEntityManagerFactoryBean schemaSpecificEntityManagerFactory( EntityManagerFactoryBuilder builder, @Qualifier("schemaSpecificDataSource") DataSource dataSource) { HashMap<String, Object> properties = new HashMap<>(); properties.put("hibernate.hbm2ddl.auto", "update"); properties.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect"); return builder.dataSource(dataSource).properties(properties) .persistenceUnit("SchemaSpecific").build(); } @Bean(name = "schemaSpecificTransactionManager") public PlatformTransactionManager schemaSpecificTransactionManager( @Qualifier("schemaSpecificEntityManagerFactory") EntityManagerFactory schemaSpecificEntityManagerFactory) { return new JpaTransactionManager(schemaSpecificEntityManagerFactory); } }

现在,可以从控制器中的 API 逻辑内部调用在 BuyerSchemaDataSource 中定义的 setSchema() 方法。

这看起来是一个糟糕的解决方法,但我没有找到比这更好的方法,并且感谢所有建议/编辑。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM