spring - 多个数据源的 Hikaricp 配置

标签 spring spring-boot hikaricp

我有一个多数据库应用程序。用户可以在登录页面选择数据库。

然后数据库正在路由选定的数据库,感谢 Spring 的 AbstractRoutingDataSource。

我想使用 HikariCP,但它需要 dataSourceUrl。但我的数据源 URL 会动态变化。如何为多个数据库配置 Hikaricp?

文件application.properties:

#database1 properties
app.database1.connection.url = url1
app.database1.connection.username = sameusername
app.database1.connection.password = samepassword
#database2 properties
app.database2.connection.url = url2
app.database2.connection.username = sameusername
app.database2.connection.password = samepassword

我的数据源配置类示例:

public class DataSourceConfiguration {

    @Autowired(required = false)
    private PersistenceUnitManager persistenceUnitManager;

    @Bean
    @ConfigurationProperties(prefix = "app.database1.connection")
    public DataSource database1DataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties(prefix = "app.database2.connection")
    public DataSource database2DataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @Primary
    public DataSource appDataSource() {
        DataSourceRouter router = new DataSourceRouter();
        final HashMap<Object, Object> map = new HashMap<>(3);
        map.put(DatabaseEnvironment.DATABASE1, database1DataSource());
        map.put(DatabaseEnvironment.DATABASE2, database2DataSource());
        router.setTargetDataSources(map);
        return router;
    }

    @Bean
    @Primary
    @ConfigurationProperties("app.connection.jpa")
    public JpaProperties appJpaProperties() {
        return new JpaProperties();
    }

    private JpaVendorAdapter createJpaVendorAdapter(JpaProperties jpaProperties) {
        AbstractJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
        adapter.setShowSql(jpaProperties.isShowSql());
        adapter.setDatabase(jpaProperties.getDatabase());
        adapter.setDatabasePlatform(jpaProperties.getDatabasePlatform());
        adapter.setGenerateDdl(jpaProperties.isGenerateDdl());
        return adapter;
    }

我的 session 范围类而不是上下文持有者:

@Component
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class PreferredDatabaseSession implements Serializable {

    /**
     *
     */
    private static final long serialVersionUID = 1L;

    private DatabaseEnvironment preferredDb;

    public DatabaseEnvironment getPreferredDb() {
        return preferredDb;
    }

    public void setPreferredDb(DatabaseEnvironment preferredDb) {
        this.preferredDb = preferredDb;
    }

}

最佳答案

如果我正确理解您的要求,您打算定义两个数据源,并且对于给定的请求,您希望根据某些条件将查询路由到特定数据源。

解决办法是:

文件application.properties

#database1 properties
app.database1.connection.url = url1
app.database1.connection.username = username1
app.database1.connection.password = password1
#database2 properties
app.database2.connection.url = url2
app.database2.connection.username = username2
app.database2.connection.password = password2
#default
default.datasource.key=dataSource1

文件CommonRoutingDataSource.java

public class CommonRoutingDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSourceName();
    }

    public void initDataSources(final DataSource dataSource1, final DataSource dataSource2,
            final String defaultDataSourceKey) {
        final Map<Object, Object> dataSourceMap = new HashMap<Object, Object>();
        dataSourceMap.put("dataSource1", dataSource1);
        dataSourceMap.put("dataSource2", dataSource2);
        this.setDefaultTargetDataSource(dataSourceMap.get(defaultDataSourceKey));
        this.setTargetDataSources(dataSourceMap);
    }

}

文件DataSourceContextHolder.java

public class DataSourceContextHolder {

    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    private DataSourceContextHolder() {
        // Private no-op constructor
    }

    public static final void setDataSourceName(final String dataSourceName) {
        Assert.notNull(dataSourceName, "dataSourceName cannot be null");
        contextHolder.set(dataSourceName);
    }

    public static final String getDataSourceName() {
        return contextHolder.get();
    }

    public static final void clearDataSourceName() {
        contextHolder.remove();
    }

}

文件DataSourceConfig.java

public class DataSourceConfig {

    @Autowired
    private Environment env;

    @Autowired
    @Bean(name = "dataSource")
    public DataSource getDataSource(final DataSource dataSource1, final DataSource dataSource2) {
        final CommonRoutingDataSource dataSource = new CommonRoutingDataSource();
        dataSource.initDataSources(dataSource1, dataSource2, env.getProperty("default.datasource.key"));
        return dataSource;
    }

    @Bean(name = "dataSource1")
    public DataSource getDataSource1() throws SQLException {
        // The exact DataSource class imported shall be as per your requirement - HikariCP, or Tomcat etc.
        final DataSource dataSource = new DataSource();
        dataSource.setDriverClassName();
        dataSource.setUrl(env.getProperty("app.database1.connection.url"));
        // Set all data source attributes from the application.properties file
        return dataSource;
    }

    @Bean(name = "dataSource2")
    public DataSource getDataSource2() throws SQLException {
        // The exact DataSource class imported shall be as per your requirement - HikariCP, or Tomcat etc.
        final DataSource dataSource = new DataSource();
        dataSource.setDriverClassName();
        dataSource.setUrl(env.getProperty("app.database2.connection.url"));
        // set all data source attributes from the application.properties file
        return dataSource;
    }

}

现在,在代码中的某个位置(方面或 Controller ),您需要有条件地动态设置数据源:

DataSourceContextHolder.setDataSourceName("dataSource1");

注意:最好将数据源名称声明为枚举,而不是字符串“dataSource1”、“dataSource2”等。

关于spring - 多个数据源的 Hikaricp 配置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54105576/

相关文章:

java - Spring MVC 重写 ResourceHttpRequestHandler

java - 使用 JPA 将 Spring Boot 连接到 MySQL 时出错

Spring & JUnit 5 - 当 @AfterAll 必须是静态时如何清除数据库

postgresql - 如何用Doobie正确处理Hikari连接池

java - 使用 MySQL 和 JDBC 准备语句缓存

java - 实体不匹配中的共享PK

java - @Schedule Cron 表达式根据属性文件中的日光保存进行调整

java - 多线程环境下的Spring状态机

java - 如何使用 spring boot websocket 发送二进制文件?

mysql - HikariCP 多个池中的连接数