Spring configures multiple data sources and implements the dynamic switching example

  • 2020-06-19 10:19:07
  • OfStack

1. Configure two different data sources as follows (since the project is using an druid database connection, the configuration can be a bit more complex) :


<!--  Data source configuration 1 --> 
  <bean id="testDataSource1" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">  
   <property name="driverClassName" value="${db.driver}" /> 
    <property name="url" value="${unity.db.jdbc.url}" />  
   <property name="username" value="${db.login.name}"></property> 
   <property name="password" value="${db.login.password}" /> 
   <property name="filters" value="${db.filters}"></property> 
   <property name="maxActive" value="${db.pool.maxActive}"></property> 
   <property name="initialSize" value="${db.pool.initialSize}"></property> 
   <property name="minIdle" value="${db.pool.minIdle}"></property> 
   <property name="maxWait" value="${db.maxWait}"></property>   
   <property name="timeBetweenEvictionRunsMillis" value="${db.timeBetweenEvictionRunsMillis}"></property> 
   <property name="minEvictableIdleTimeMillis" value="${db.minEvictableIdleTimeMillis}"></property> 
   <property name="validationQuery" value="${db.validationQuery}"></property> 
   <property name="testWhileIdle" value="${db.testWhileIdle}"></property> 
   <property name="testOnBorrow" value="${db.testOnBorrow}"></property> 
   <property name="testOnReturn" value="${db.testOnReturn}"></property> 
   <property name="poolPreparedStatements" value="${db.poolPreparedStatements}"></property> 
   <property name="maxOpenPreparedStatements" value="${db.maxOpenPreparedStatements}"></property> 
   <!--  Monitoring database  --> 
    <property name="proxyFilters"> 
      <list> 
        <ref bean="log-filter" /> 
      </list>  
    </property> 
    
 </bean> 

<!--  Data source configuration 2 --> 
 <bean id="testDataSource2" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">  
  <property name="driverClassName" value="${db.driver}" /> 
   <property name="url" value="${pub.db.jdbc.url}" />  
  <property name="username" value="${db.login.name}"></property> 
  <property name="password" value="${db.login.password}" /> 
  <property name="filters" value="${db.filters}"></property> 
  <property name="maxActive" value="${db.pool.maxActive}"></property> 
  <property name="initialSize" value="${db.pool.initialSize}"></property> 
  <property name="minIdle" value="${db.pool.minIdle}"></property> 
  <property name="maxWait" value="${db.maxWait}"></property>   
  <property name="timeBetweenEvictionRunsMillis" value="${db.timeBetweenEvictionRunsMillis}"></property> 
  <property name="minEvictableIdleTimeMillis" value="${db.minEvictableIdleTimeMillis}"></property> 
  <property name="validationQuery" value="${db.validationQuery}"></property> 
  <property name="testWhileIdle" value="${db.testWhileIdle}"></property> 
  <property name="testOnBorrow" value="${db.testOnBorrow}"></property> 
  <property name="testOnReturn" value="${db.testOnReturn}"></property> 
  <property name="poolPreparedStatements" value="${db.poolPreparedStatements}"></property> 
  <property name="maxOpenPreparedStatements" value="${db.maxOpenPreparedStatements}"></property> 
  <!--  Monitoring database  --> 
   <property name="proxyFilters"> 
     <list> 
       <ref bean="log-filter" /> 
     </list>  
   </property> 
   
</bean> 

2. Define a class to inherit AbstractRoutingDataSource to implement determineCurrentLookupKey method, which can realize the dynamic switch of the database, as follows:


public class DynamicDataSource extends AbstractRoutingDataSource { 
  @Override 
  protected Object determineCurrentLookupKey() { 
    return DataSourceContextHolder.getDataSourceType(); 
  } 
} 

3. Define a utility class that can set the variable of the current thread and use it to set the corresponding data source name:


public class DataSourceContextHolder { 
  private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); 
  /** 
   * @Description:  Set the data source type  
   * @param dataSourceType  Database type  
   * @return void 
   * @throws 
   */ 
  public static void setDataSourceType(String dataSourceType) { 
    contextHolder.set(dataSourceType); 
  } 
   
  /** 
   * @Description:  Get data source type  
   * @param 
   * @return String 
   * @throws 
   */ 
  public static String getDataSourceType() { 
    return contextHolder.get(); 
  } 
   
  /** 
   * @Description:  Clear data source type  
   * @param 
   * @return void 
   * @throws 
   */ 
  public static void clearDataSourceType() { 
    contextHolder.remove(); 
  } 
} 

Then configure in spring as follows:


 <!--  write spring  Configuration file for most source mapping relationships  --> 
<bean class="com.sino.access.database.DynamicDataSource" id="dataSource"> 
  <property name="targetDataSources"> 
    <map key-type="java.lang.String"> 
      <entry value-ref="testDataSource1" key="<span style="font-family: Arial, Helvetica, sans-serif;">testDataSource1</span><span style="font-family: Arial, Helvetica, sans-serif;">"></entry></span> 
      <entry value-ref="testDataSource2" key="testDataSource2"></entry> 
    </map> 
  </property> 
  <property name="defaultTargetDataSource" ref="testDataSource1"> 
  </property> 
</bean> 
 </bean>

In this way, the key corresponding to the two data sources is testDataSource1 and testDataSource2, and the default database is testDataSource.

4. After completing the above steps, if there is no transaction management of the database, the dynamic switch of the database can already be realized. However, if it involves transaction management of the database, you need to open and switch the database in the database transaction,

Otherwise the database switch will only take effect at the next database operation. You can define an aop processing class to switch the database before the database transaction is started, as follows:


public class DataSourceAspect implements MethodBeforeAdvice,AfterReturningAdvice  
{ 
 
  @Override 
  public void afterReturning(Object returnValue, Method method, 
      Object[] args, Object target) throws Throwable { 
    // TODO Auto-generated method stub 
    DataSourceContextHolder.clearDataSourceType(); 
  } 
 
  @Override 
  public void before(Method method, Object[] args, Object target) 
      throws Throwable { 
   
    if (method.isAnnotationPresent(DataSource.class))  
    { 
      DataSource datasource = method.getAnnotation(DataSource.class); 
      DataSourceContextHolder.setDataSourceType(datasource.name()); 
    } 
    else 
    { 
      DataSourceContextHolder.setDataSourceType(SinoConstant.DataSourceType.unityDataSource.toString()); 
    } 
     
  } 
} 

5. Set the execution order of database transaction slices and switch database slices, as follows:


<aop:config> 
  <aop:pointcut id="transactionPointCut" expression="execution(* com.test.service.*.*(..))" /> 
  <aop:advisor pointcut-ref="transactionPointCut" 
    advice-ref="txAdvice" order="2" /> 
  <aop:advisor advice-ref="dataSourceExchange" pointcut-ref="transactionPointCut" order="1"/> 
</aop:config> 

Using the order property of aop to set the order of execution, spring database with transaction management is dynamically switched.


Related articles: