I have a Spring application, i want to change the data source dynamically,ie. when input a DS URL, the Spring beans and all dependency will get updated automatically.I know this is somewhat strange, but anyway i want to achieve that. My Spring configuration as following:
<bean id="majorDataSource" class="org.postgresql.ds.PGSimpleDataSource"> <property name="serverName" value="${jdbc.serverName}" /> <property name="portNumber" value="${jdbc.portNumber}" /> <property name="user" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> <property name="databaseName" value="${jdbc.databaseName}" /> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="majorDataSource"/> </bean> <tx:annotation-driven transaction-manager="transactionManager"/> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="majorDataSource"/> <property name="configLocation" value="classpath:sqlmap-config.xml"/> </bean>
The questions are:
The JDBC URL is stored in properties, which could be changed runtime.
Once the URL is changed, i need to re-create the data source and maybe the dependent objects. I could not figure out how to do it elegantly in Spring?
I have known that Spring did could dynamically route data source based on one key, but the data source URL is predefined in Spring and will not change runtime. It is not my case.
To access the Relational Database by using JdbcTemplate in Spring Boot application, we need to add the Spring Boot Starter JDBC dependency in our build configuration file. Then, if you @Autowired the JdbcTemplate class, Spring Boot automatically connects the Database and sets the Datasource for the JdbcTemplate object.
To configure your own DataSource , define a @Bean of that type in your configuration. Spring Boot reuses your DataSource anywhere one is required, including database initialization. If you need to externalize some settings, you can bind your DataSource to the environment (see “Section 25.8.
entityManagerFactoryRef. Configures the name of the EntityManagerFactory bean definition to be used to create repositories discovered through this annotation. char. escapeCharacter. Configures what character is used to escape the wildcards _ and % in derived queries with contains, startsWith or endsWith clauses.
You can use spring's AbstractRoutingDataSource by extending it and overriding the method determineCurrentLookupKey()
that should return the key referencing the datasource's spring bean to be used.
Take a look at this blog article on spring source's blog which will show you an example of how to use that feature.
Basically to answer your questions, what you will need to do is to define the two datasources as different spring bean in your XML config. There is no need to create one dynamically, spring will load both, and use one or the other dynamically depending on your criteria in the determineCurrentLookupKey()
method.
This would lead to something like:
XML config
<!-- first data source --> <bean id="majorDataSource" class="org.postgresql.ds.PGSimpleDataSource"> <property name="serverName" value="${jdbc.major.serverName}" /> <property name="portNumber" value="${jdbc.major.portNumber}" /> <property name="user" value="${jdbc.major.username}" /> <property name="password" value="${jdbc.major.password}" /> <property name="databaseName" value="${jdbc.major.databaseName}" /> </bean> <!-- second data source --> <bean id="minorDataSource" class="org.postgresql.ds.PGSimpleDataSource"> <property name="serverName" value="${jdbc.minor.serverName}" /> <property name="portNumber" value="${jdbc.minor.portNumber}" /> <property name="user" value="${jdbc.minor.username}" /> <property name="password" value="${jdbc.minor.password}" /> <property name="databaseName" value="${jdbc.minor.databaseName}" /> </bean> <!-- facade data source --> <bean id="dataSource" class="blog.datasource.CustomerRoutingDataSource"> <property name="targetDataSources"> <map> <entry key="MINOR" value-ref="minorDataSource"/> <entry key="MAJOR" value-ref="majorDataSource"/> </map> </property> <property name="defaultTargetDataSource" ref="majorDataSource"/> </bean> <!-- wiring up --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <tx:annotation-driven transaction-manager="transactionManager"/> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="configLocation" value="classpath:sqlmap-config.xml"/> </bean>
Java
public class MyRoutingDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { // get the current url HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); if (request.getRequestURL().toString().endsWith("/minor")) return "MINOR"; else return "MAJOR"; } }
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