多个数据源的Hikaricp配置

时间:2019-01-09 08:04:35

标签: 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;
}

我的会话作用域类而不是上下文持有者:

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

}

1 个答案:

答案 0 :(得分:1)

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

解决方案是:

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

}

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

DataSourceContextHolder.setDataSourceName("dataSource1");

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