当前位置: 首页 > 知识库问答 >
问题:

(spring-布特&Spring Data JPA)如何快速更改数据源?

漆雕硕
2023-03-14

对于我的新项目,我构建了一个基本的rest api来根据客户机请求返回数据。但是,客户端必须选择他所选择的数据库作为HTTP GET请求的参数。

现在我的问题是,我不知道如何使用Sprint-Boot实现这一点。我知道我们可以提供许多不同的数据源,但是我们如何在检查请求之后更改想要的数据源呢?

下面是我的数据源配置,它工作得很好:

@Configuration
public class DataSourceConfig {

    @Bean
    @Primary
    @ConfigurationProperties(prefix="datasource.dev21")
    public DataSource dev21DataSource() throws SQLException {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties(prefix="datasource.dev22")
    public DataSource dev22DataSource() throws SQLException {
        return DataSourceBuilder.create().build();
    }
} 

如果我想在dev21和dev22之间动态切换,我该怎么做?我读过关于类AbstractRoutingDataSource的文章,但不知道如何使用它。

共有2个答案

钱欣悦
2023-03-14

在没有对其进行测试的情况下,只要简单地浏览一下javadoc,就可以使用类似的方法

@Configuration
public class DataSourceConfig {

    public DataSource dev21DataSource() throws SQLException {
        return DataSourceBuilder.create().build();
    }

    public DataSource dev22DataSource() throws SQLException {
        return DataSourceBuilder.create().build();
    }

    @Bean
    public DataSource dataSource() throws SQLException {
        RoutingDataSource ds = new RoutingDataSource();

        DataSource ds21 = dev21DataSource();
        DataSource ds22 = dev22DataSource();

        Map dataSources = new HashMap();
        dataSources.put(1, ds21);
        dataSources.put(2, ds22);

        ds.setDefaultTargetDataSource(ds21);
        ds.setTargetDataSources(dataSources);

        return ds;
    }
}

public class RoutingDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        if (true) { //Should probably be some thread/tracaction/request safe check
            return 1;
        } else {
            return 2;
        }
    }
}

如果需要类的其他行为,则可能希望override其他方法。

邢修明
2023-03-14

它应该遵循这样的方针:

// create class to hold the "key" to choose your datasource
// you will determine it from your GET or POST request
// It uses ThreadLocal so you will get one per each request
public class SomeRequestContext {
    private static ThreadLocal<Object> keyToChoseDataSource = new ThreadLocal<>();
    public static void setKeyToChoseDataSource(Object key) {
        keyToChoseDataSource.set(key);
    }
    public static Object getKeyToChoseDataSource() {
        return keyToChoseDataSource.get();
    }
}

// This is you AbstractRoutingDataSource implementation that will
// get the key out of the context class above
public class MultiDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return SomeRequestContext.getKeyToChoseDataSource();
    }
}

在您的配置中:

    // Here you just put all your data sources into the AbstractRoutingDataSource implementation
    @Bean 
    @Primary 
    public DataSource dataSource() {
      MultiDataSource dataSource = new MultiDataSource();
      dataSource.setDefaultTargetDataSource(someDefaultDataSource());
      Map<Object,DataSource> resolvedDataSources = new HashMap<Object,DataSource>();
      resolvedDataSources.put("dev21",buildDataSource21());
      resolvedDataSources.put("dev22",buildDataSource22());
      // ...etc...
      dataSource.setTargetDataSources(resolvedDataSources);
      dataSource.afterPropertiesSet();
      return dataSource;
    }

在你的控制器里

@Controller
public class YourController {
    @Autowired
    private YourRepository yourRepository;
    @RequestMapping(path = "/path", method= RequestMethod.POST)
    public ResponseEntity<?> createStuff() {
        TenantContext.setCurrentTenant(tenantName);
        SomeRequestContext.setKeyToChoseDataSource(getKeyFromRequest()); // this will be dev21 or dev22 or whatever

        SomeStuff stuff = new SomeStuff();
        yourRepository.save(stuff); // will be saved to the correct database
        return ResponseEntity.ok(stuff);
    }
}
 类似资料:
  • 问题内容: 在MySQL手册的MySQL涵盖这一点。 通常,我只是转储数据库,然后使用新名称重新导入它。对于大型数据库,这不是一个选择。显然 做不好的事情,仅存在于少数几个版本中,并且总体而言是个坏主意。 这需要与InnoDB一起使用,后者存储的内容与MyISAM完全不同。 问题答案: 对于 InnoDB ,以下方法似乎有效:创建新的空数据库,然后将每个表依次重命名为新数据库: 之后,您将需要调整

  • 问题内容: 现在我知道苹果不推荐这样做。 通常,您不应在应用程序内更改iOS系统语言(通过使用AppleLanguages首选项键)。这违反了在设置应用程序中切换语言的基本iOS用户模型,并且还使用了未记录的首选项键,这意味着将来某个时候,键名可能会更改,这会破坏您的应用程序。 但是,这是一个可以随时更改语言的应用程序,请相信我。我也知道有人问过这个问题:在运行iOS时以编程方式实时更改语言。但是

  • 问题内容: 如何在with中检测数据的变化?以下代码不进行任何检测。 我在声明: 谢谢斯科特 问题答案: 您需要设置UITextView 委托并在其中实现textViewDidChange:方法。不幸的是,我不知道是否可以在线获得快速文档。所有链接都转到Objective- C文档。 该代码将如下所示:( 已针对SWIFT 4.2更新 )

  • 我正在使用Fast.API我需要一个API来允许用户发布任何数据,键/值-我使用它来允许用户将自定义配置文件键/值字段添加到配置文件,其中键是字符串类型,值是字符串、数字、布尔值。 如何添加此类路由? 我正在使用这条路线,但不起作用: 我希望能够将任何一对键/值发布到该路由。有什么方法可以用FastAPI来实现吗? 非常感谢。

  • 问题内容: 不久前我问过类似的问题。有人问我怎样才能变成这样的数组: 对此: 但是现在我想把相同的数组变成这个: 假设所有子数组具有相同的长度。 如果您尚未注意到,结果中的前三项就是这三个子数组中的第一项。结果中的第四,第五和第六项是每个子数组的第二项。 如果您仍然不了解,也许这会有所帮助: 原始数组: 结果: 此刻,我有这个: 我认为那不是很花钱。如何快速进行此操作? 为了避免成为XY问题,这就

  • 问题是如何在例如主详细表上实现数据变化的跟踪,即Spring Boot/Spring数据中一对多关系中的两个实体。 在存储数据后,能够获得主实体及其特定版本的详细信息,并具有将其还原到特定版本的功能。