I'm pretty new to Spring boot and I'm working on a new app that needs to be able to connect to one of multiple available databases. Based on a user's credentials I'll determine what database to connect to so I need the ability to change connections dynamically at runtime. I found an old Spring blog that outlined a solution for this here that advocates using AbstractRoutingDataSource that routes getConnection() calls to other DataSources based on lookup keys. I tried to follow the blog carefully but I keep getting the following error.
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration':
Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'dataSource' defined in robb.referencecomponent.Application:
Invocation of init method failed; nested exception is org.springframework.jdbc.datasource.lookup.DataSourceLookupFailureException:
Failed to look up JNDI DataSource with name 'dev1DataSource'; nested exception is javax.naming.NoInitialContextException:
Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
And here is the code for my Application.java class:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
@ConfigurationProperties(prefix="app.dev1.datasource")
public DataSource dev1DataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix="app.dev2.datasource")
public DataSource dev2DataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public DataSource dataSource() {
return new RoutingDataSource();
}
public class RoutingDataSource extends AbstractRoutingDataSource {
public RoutingDataSource() {
super();
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DbLocation.DEV1, "dev1DataSource");
targetDataSources.put(DbLocation.DEV2, "dev2DataSource");
setTargetDataSources(targetDataSources);
setDefaultTargetDataSource(DbLocation.DEV2);
}
@Override
protected Object determineCurrentLookupKey() {
return ClientContextHolder.getDbLocation();
}
}
}
And I set the DataSource configurations in a properties file like so:
app.dev1.datasource.url=jdbc:oracle:thin:@dev1_db_url
app.dev1.datasource.username=dev1
app.dev1.datasource.password=XXXXXXX
app.dev1.datasource.driver-class-name=oracle.jdbc.OracleDriver
app.dev2.datasource.url=jdbc:oracle:thin:@dev2_db_url
app.dev2.datasource.username=dev2
app.dev2.datasource.password=XXXXXXX
app.dev2.datasource.driver-class-name=oracle.jdbc.OracleDriver
I named one of my DataSources 'dev1DataSource' and it's complaining that it can't look it up using JNDI, but when I remove the RoutingDataSource class and the dataSource() bean definition and make the dev1DataSource() bean @Primary I'm able to connect to the dev1 database fine. I'm not sure what I did wrong in porting over that older blog's code into my application, I know the beans were set up in the examples using xml but mine are set up with Java code and annotations, maybe some mistakes were made in that translation?
Does anyone have experience working with AbstractRoutingDataSource in Spring boot and have they had this sort of problem come up?
AbstractRoutingDataSource
supports multiple lookup mechanisms. The value
type of the targetDataSources
may vary, depending on the DataSourceLookup
which defaults to JNDI lookup. That's why you see a NoInitialContextException
on initialization.
You have two options how to fix your issue:
DataSource
instances instead of the dev1DataSource
/dev2DataSource
Strings. You define two DataSource
@Bean
methods, and you pass the DataSources
to the initialization of your RoutingDataSource
.DataSourceLookup
that picks up the configuration properties from Environment
. The own DataSourceLookup
would have to take care of caching the created instances and application shutdown that's why I'd recommend option 1.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With