(Spring-boot& Spring data jpa)如何动态更改数据源?

时间:2016-09-29 12:53:01

标签: rest spring-boot datasource spring-data-jpa

对于我的新项目,我构建了一个基本的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 个答案:

答案 0 :(得分:0)

没有经过测试,只是简单地浏览一下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其他方法。

答案 1 :(得分:0)

应该遵循以下方针:

// 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);
    }
}