为了账号安全,请及时绑定邮箱和手机立即绑定

Spring Boot 多个外部数据源存储在内部数据库中

Spring Boot 多个外部数据源存储在内部数据库中

MMMHUHU 2022-04-28 15:43:49
我有一个 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?
查看完整描述

2 回答

?
大话西游666

TA贡献1817条经验 获得超14个赞

设置的困难不是多个数据源,而是它们是动态的,即在运行时确定的事实。除了DataSourceJPA 使用EntityManagerFactoryTransactionManager,它们是静态确定的,即在编译时。因此,将 JPA 与动态数据源一起使用并不容易。

在 Spring Boot 2 中,您可以尝试AbstractRoutingDataSource,它允许基于某些(线程绑定)上下文将 JPA 调用路由到不同的数据源。这是一个如何使用它的示例和一个演示应用程序。

或者,您可以将设置转换为静态设置,然后使用常规的多数据源方法。缺点是“公司”列表将在编译时固定,因此可能不是您想要的。


查看完整回答
反对 回复 2022-04-28
?
不负相思意

TA贡献1777条经验 获得超10个赞

我的解决方案:


我使用@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();

    }


}

配置类创建保存在公司表中的所有数据库连接:


@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;

    }


}


查看完整回答
反对 回复 2022-04-28
  • 2 回答
  • 0 关注
  • 173 浏览

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号