Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic datasource routing - DataSource router not initialized

I'm referring to this article, in which we can use the AbstractRoutingDataSource from Spring Framework to dynamically change the data source used by the application. I'm using Mybatis (3.3.0) with Spring (4.1.6.RELEASE). I want to switch to the backup database if exception occurs while getting data from main db. In this example, i have used hsql and mysql db.

RoutingDataSource:

public class RoutingDataSource extends AbstractRoutingDataSource {

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

DataSourceContextHolder:

public class DataSourceContextHolder {

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

public static void setTargetDataSource(DataSourceEnum targetDataSource) {
    contextHolder.set(targetDataSource);
}

public static DataSourceEnum getTargetDataSource() {
    return (DataSourceEnum) contextHolder.get();
}

public static void resetDefaultDataSource() {
    contextHolder.remove();
 }
}

ApplicationDataConfig:

@Configuration
@MapperScan(basePackages = "com.sample.mapper")
@ComponentScan("com.sample.config")
@PropertySource(value = {"classpath:app.properties"},
                ignoreResourceNotFound = true)
public class ApplicationDataConfig {

@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
    PropertySourcesPlaceholderConfigurer configurer =
        new PropertySourcesPlaceholderConfigurer();
    return configurer;
}

@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean() throws Exception {
    SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
    RoutingDataSource routingDataSource = new RoutingDataSource();
    routingDataSource.setDefaultTargetDataSource(dataSource1());
    Map<Object, Object> targetDataSource = new HashMap<Object, Object>();
    targetDataSource.put(DataSourceEnum.HSQL, dataSource1());
    targetDataSource.put(DataSourceEnum.BACKUP, dataSource2());
    routingDataSource.setTargetDataSources(targetDataSource);
    sessionFactory.setDataSource(routingDataSource);
    sessionFactory.setTypeAliasesPackage("com.sample.common.domain");

    sessionFactory.setMapperLocations(
        new PathMatchingResourcePatternResolver()
            .getResources("classpath*:com/sample/mapper/**/*.xml"));

    return sessionFactory;
}

@Bean
public DataSource dataSource1() {
    return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL).addScript(
            "classpath:database/app-hsqldb-schema.sql").addScript(
            "classpath:database/app-hsqldb-datascript.sql").build();
}


@Bean
public DataSource dataSource2() {
    PooledDataSourceFactory pooledDataSourceFactory = new PooledDataSourceFactory();
    pooledDataSourceFactory.setProperties(jdbcProperties());
    return pooledDataSourceFactory.getDataSource();
}

@Bean
protected Properties jdbcProperties() {
    //Get the data from properties file
    Properties jdbcProperties = new Properties();
    jdbcProperties.setProperty("url", datasourceUrl);
    jdbcProperties.setProperty("driver", datasourceDriver);
    jdbcProperties.setProperty("username", datasourceUsername);
    jdbcProperties.setProperty("password", datasourcePassword);
    jdbcProperties.setProperty("poolMaximumIdleConnections", maxConnectionPoolSize);
    jdbcProperties.setProperty("poolMaximumActiveConnections", minConnectionPoolSize);

    return jdbcProperties;
}
}

Client:

 @Autowired
 private ApplicationMapper appMapper;

public MyObject getObjectById(String Id) {
    MyObject myObj = null;
    try{
        DataSourceContextHolder.setTargetDataSource(DataSourceEnum.HSQL);
        myObj = appMapper.getObjectById(Id);
    }catch(Throwable e){
        DataSourceContextHolder.setTargetDataSource(DataSourceEnum.BACKUP);
        myObj = appMapper.getObjectById(Id);  
    }finally{
        DataSourceContextHolder.resetDefaultDataSource();
    }
    return getObjectDetails(myObj);
}

I'm getting the following exception

### Error querying database.  Cause: java.lang.IllegalArgumentException: DataSource router not initialized

However i'm able to get things working if i'm using only one db at a time, this means there is no issue with data source configuration.

like image 976
Pankaj Avatar asked Apr 11 '26 04:04

Pankaj


1 Answers

Can you try this last line once (in same order) :-

targetDataSource.put(DataSourceEnum.HSQL, dataSource1());
targetDataSource.put(DataSourceEnum.BACKUP, dataSource2());
routingDataSource.setTargetDataSources(targetDataSource);

routingDataSource.afterPropertiesSet();
like image 65
Avis Avatar answered Apr 12 '26 17:04

Avis



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!