我终于找到了一个可行的解决方案。该解决方案主要使用来自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 逻辑内部调用。
这看起来是一种不好的解决方法,但我没有找到比这更好的方法,所有建议/编辑都表示赞赏。