【问题标题】:Set schema name at run time using REST API input in spring boot based application在基于 Spring Boot 的应用程序中使用 REST API 输入在运行时设置模式名称
【发布时间】:2020-12-12 04:47:57
【问题描述】:

postgres 数据库中,schema name 设置为 buyer id(因此,如果买家 id 为 buyer_2213 那么架构名称将是 buyer_2213)。这些模式有多个表,并且这些表对所有模式都有共同的结构。

现在,我正在使用 spring boot 编写 REST API 以从这些模式中获取买家数据,但由于 schema name 依赖于 buyer id,因此我必须编写手动查询并且无法为此使用 JPA 功能。

有没有办法使用 REST API 请求参数将 架构名称 设置为实体。那么,在下面的实体中,我们可以使用在 BuyerController 中定义的 API 调用中传递的 buyerId 来设置 schema

  @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 输入的任何其他方式)。因此我找不到与this 的相关性

【问题讨论】:

    标签: java spring postgresql spring-boot


    【解决方案1】:

    我终于找到了一个可行的解决方案。该解决方案主要使用来自here 的配置,但更具体地满足我的问题要求。

    这个想法显然是使用AbstractDataSource 并且数据源配置与here 中显示的非常相似,只是模式名称将使用 setter 设置,可以从内部 API 逻辑调用.
    首先,我们需要编写一个 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);
        }
    }
    
    

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

    这看起来是一种不好的解决方法,但我没有找到比这更好的方法,所有建议/编辑都表示赞赏。

    【讨论】:

      猜你喜欢
      • 2011-12-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-28
      • 2023-04-03
      • 2013-09-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多