我有一个spring boot项目,并且我有一个内部数据库,其中包含application.properties上的配置。在该数据库中,我有一个Company表,其中包含与外部数据库的连接信息(所有外部数据库具有相同的结构)。
我创建了一个在需要时创建数据源的类:
public class PgDataSource {
private static Map<Long, DataSource> dataSourceMap = new HashMap<>();
private static void createDataSource(Company company) {
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setMaximumPoolSize(10);
hikariConfig.setMinimumIdle(1);
hikariConfig.setJdbcUrl("jdbc:postgresql://"+company.getUrl()+"/"+company.getIdClient());
hikariConfig.setUsername(company.getUsername());
hikariConfig.setPassword(company.getPassword());
dataSourceMap.put(company.getId(), new HikariDataSource(hikariConfig));
}
public static DataSource getDataSource(Company company) {
if (!dataSourceMap.containsKey(company.getId()))
createDataSource(company);
return dataSourceMap.get(company.getId());
}
}
您能否告诉我该解决方案是否是最佳解决方案,以及我是否可以在该解决方案中使用JPA?
谢谢
答案 0 :(得分:0)
设置的难点不是多个数据源,而是它们是动态的,即在运行时确定的事实。
JPA除了使用DataSource
之外,还使用EntityManagerFactory
和TransactionManager
,它们是静态确定的,即在编译时。因此,将JPA与动态数据源一起使用并不容易。
在Spring Boot 2中,您可以尝试使用AbstractRoutingDataSource,它允许基于某些(线程绑定)上下文将JPA调用路由到另一个数据源。 Here's以及如何使用它和demo应用程序的示例。
或者,您可以将设置转换为静态设置,然后使用常规的multiple datasource方法。缺点是“公司”列表将在编译时固定,因此可能不是您想要的。
答案 1 :(得分:0)
谢谢!
我的解决方案:
我使用@Primary注释为我的本地数据库创建了第一个数据源。
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "localEntityManagerFactory",
basePackages = {"fr.axygest.akostaxi.local"}
)
public class LocalConfig {
@Primary
@Bean(name = "dataSource")
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
@Primary
@Bean(name = "localEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
EntityManagerFactoryBuilder builder,
@Qualifier("dataSource") DataSource dataSource) {
return builder
.dataSource(dataSource)
.packages("fr.axygest.akostaxi.local.model")
.persistenceUnit("local")
.build();
}
@Primary
@Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager(
@Qualifier("localEntityManagerFactory") EntityManagerFactory
entityManagerFactory
) {
return new JpaTransactionManager(entityManagerFactory);
}
}
接下来,对于保存在本地数据库的company表中的x个外部数据库,我使用AbstractRoutingDataSource
我将当前上下文存储为ThreadLocal:
public class ThreadPostgresqlStorage {
private static ThreadLocal<Long> context = new ThreadLocal<>();
public static void setContext(Long companyId) {
context.set(companyId);
}
public static Long getContext() {
return context.get();
}
}
我定义了RoutingSource来扩展AbstractRoutingDataSource:
public class RoutingSource extends AbstractRoutingDataSource
{
@Override
protected Object determineCurrentLookupKey() {
return ThreadPostgresqlStorage.getContext();
}
}
然后config类创建保存在company表中的所有数据库连接:
@Configuration
@EnableJpaRepositories(
basePackages = {"fr.axygest.akostaxi.postgresql"},
entityManagerFactoryRef = "pgEntityManager"
)
@EnableTransactionManagement
public class PgConfig {
private final CompanyRepository companyRepository;
@Autowired
public PgConfig(CompanyRepository companyRepository) {
this.companyRepository = companyRepository;
}
@Bean(name = "pgDataSource")
public DataSource pgDataSource() {
RoutingSource routingSource = new RoutingSource();
List<Company> companies = companyRepository.findAll();
HashMap<Object, Object> map = new HashMap<>(companies.size());
companies.forEach(company -> {
map.put(company.getId(), createDataSource(company));
});
routingSource.setTargetDataSources(map);
routingSource.afterPropertiesSet();
return routingSource;
}
@Bean(name = "pgEntityManager")
public LocalContainerEntityManagerFactoryBean pgEntityManager(
EntityManagerFactoryBuilder builder,
@Qualifier("pgDataSource") DataSource dataSource) {
return builder
.dataSource(dataSource)
.packages("fr.axygest.akostaxi.postgresql.model")
.persistenceUnit("pg")
.properties(jpaProperties())
.build();
}
private DataSource createDataSource(Company company) {
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setMaximumPoolSize(10);
hikariConfig.setMinimumIdle(1);
hikariConfig.setJdbcUrl("jdbc:postgresql://" + company.getUrl() + "/" + company.getIdClient());
hikariConfig.setUsername(company.getUsername());
hikariConfig.setPassword(company.getPassword());
return new HikariDataSource(hikariConfig);
}
private Map<String, Object> jpaProperties() {
Map<String, Object> props = new HashMap<String, Object>();
props.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
return props;
}
}