Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

dynamically change Spring data source

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:

  1. The JDBC URL is stored in properties, which could be changed runtime.

  2. 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.

like image 696
Simon Wang Avatar asked Nov 22 '12 07:11

Simon Wang


People also ask

How do I change DataSource in spring boot?

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.

How do you configure a DataSource in spring?

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.

What is entityManagerFactoryRef?

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.


1 Answers

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";    } } 
like image 107
Alex Avatar answered Sep 24 '22 02:09

Alex