Configuration and use of multiple data sources in Spring

Recently developed a small function of data synchronization, which needs to synchronize data from the Oracle database of host A to the Oracle database of host B. Of course, it is best to use dmp scripts or SQL scripts, but for the heterogeneous table structure on both sides, direct import is not feasible. Then it is not feasible to use stored procedures when real-time synchronization is required. Writing a small program for data synchronization is a good choice. It is necessary to use the encapsulation and connection pool of the framework. Spring is the first choice. Here we also need Spring's multi-data source connection configuration method. In fact, when developing a project, a project may use more than one data source. In order to improve the horizontal scalability of the database, it is necessary to manage multiple database instances and configure multiple data sources.

     1. Configure multiple data sources
     Here, the data sources of two c3p0 database connection pools are used as examples. Databases using c3p0 under the Spring framework need to be added to the support package c3p0-0.9.1.2.jar (now the latest). Take the data synchronization project as an example:
   the connection pool data source configuration of the data source library:
Xml code Collection code
<bean id="dataSourceFrom" class="com.mchange.v2.c3p0.ComboPooledDataSource"> 
    <property name="driverClass" value="${jdbc.driver}" /> 
    <property name="jdbcUrl" value="${jdbc.from.url}" /> 
    <property name="user" value="${jdbc.from.username }" 
    <property name="password" value="${jdbc.from.password}" /> 
    <property name="autoCommitOnClose" value="true" /> 
    <property name="checkoutTimeout" value="${cpool.checkoutTimeout}" /> 
    <property name="initialPoolSize" value="${cpool.minPoolSize}" /> 
    <property name="minPoolSize" value="${cpool.minPoolSize}" /> 
    <property name="maxPoolSize" value="${cpool.maxPoolSize}" /> 
    <property name="maxIdleTime" value="${cpool.maxIdleTime}" /> 
    <property name="acquireIncrement" value="${cpool.acquireIncrement}" /> 
    <property name="maxIdleTimeExcessConnections" value="${cpool.maxIdleTimeExcessConnections}" />  Xml code Collection code The connection pool data source configuration of the data insertion library:
</bean> 


<bean id="dataSourceTo" class="com.mchange.v2.c3p0.ComboPooledDataSource"> 
    <property name="driverClass" value="${jdbc.driver}" /> 
    <property name="jdbcUrl" value="${jdbc.to.url}" /> 
    <property name="user" value="${jdbc.to.username}" /> 
    <property name="password" value="${jdbc.to.password}" /> 
    <property name="autoCommitOnClose" value="true" /> 
    <property name="checkoutTimeout" value="${cpool.checkoutTimeout}" /> 
    <property name="initialPoolSize" value="${cpool.minPoolSize}" /> 
    <property name="minPoolSize" value="${cpool.minPoolSize}" /> 
    <property name="maxPoolSize" value="${cpool.maxPoolSize}" /> 
    <property name="maxIdleTime" value="${cpool.maxIdleTime}" /> 
    <property name="acquireIncrement" value="${cpool.acquireIncrement}" /> 
    <property name="maxIdleTimeExcessConnections" value="${ cpool.maxIdleTimeExcessConnections}" /> 
</bean> 
   Note: The above url, user, password and other values ​​are obtained from jdbc.properties under the classpath.
   Get the value in the property file through Spring for use by the configuration file:
Xml code Collection code
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 
    <property name="locations" value="classpath:jdbc .properties" /> 
</bean> 

     2. Extend Spring's AbstractRoutingDataSource abstract class to implement dynamic data sources.
    The abstract method determineCurrentLookupKey in AbstractRoutingDataSource is the core of implementing the route of the data source. Override this method here.
Java code Collection code
public class DynamicDataSource extends AbstractRoutingDataSource{ 
 
    @Override 
    protected Object determineCurrentLookupKey() { 
        return DBContextHolder.getDBType(); 
    } 

   The context DbContextHolder is a thread-safe ThreadLocal, the specific code is as follows:
Java code Collection code
public class DBContextHolder{ 
    public static final String DATA_SOURCE_FROM = "dataSourceFrom"; 
    public static final String DATA_SOURCE_TO = "dataSourceTo"; 
     
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); 
     
    public static void setDBType(String dbType) { 
        contextHolder.set(dbType) ; 
    } 
     
    public static String getDBType() { 
        return contextHolder.get(); 
    } 
     
    public static void clearDBType() { 
        contextHolder.remove(); 
    } 


    3. Configure dynamic data source
Add DynamicDataSource Bean to Spring's context xml configuration file, At the same time, configure the Map mapping of the targetDataSources (multi-data source target) property of DynamicDataSource.
Xml code Collection code
<bean id="dynamicDataSource" class="datasource.DynamicDataSource" > 
    <!-- Associate data sources in the form of key-value--> 
    <property name="targetDataSources"> 
        <map> 
            <entry value -ref="dataSourceFrom" key="dataSourceFrom"></entry> 
            <entry value-ref=" 
        </map> 
    </property> 
    <property name="defaultTargetDataSource" ref="dataSourceFrom" /> 
</bean>  

    4. In the example of using dynamic data source
    , DynamicDataSource inherits from AbstractRoutingDataSource, and AbstractRoutingDataSource inherits from org.springframework. jdbc.datasource.AbstractDataSource, AbstractDataSource implements a unified DataSource interface, so DynamicDataSource can also be used as a DataSource.
Configuration example of using dynamic data source in Spring's JdbcTemplate:
Xml code Collection code
<!-- JdbcTemplate using dynamic data source configuration --> 
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" > 
    <property name="dataSource"> 
        <ref bean="dynamicDataSource" /> 
    </property> 

 

<bean id="sqlBaseDAO" class="com.whty.dao.BaseDAOImpl"> 
    <property name="jdbcTemplate"> 
        <ref bean="jdbcTemplate" /> 
    </property> 
</bean> 
in ORM framework hibernate Example of using configuration:
Xml code Collection code
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> 
    <!-- Same as normal dataSource usage --> 
    <property name="dataSource " ref="dynamicDataSource" /> 
    <property name="configLocations" value="classpath:hibernate.cfg.xml" /> 
    <property name="hibernateProperties"> 
        <  props> 
            <prop key="hibernate.dialect">${hibernate.dialect}</prop>    
    </property> 
</bean> 
  
    5. Transaction management
When using a dynamic data source, it can be seen that there is almost no difference in the use configuration compared to when using a single data source, and there is no difference in the configuration of the ongoing transaction management:
transactions using Spring's JdbcTemplate Management configuration example:
Xml code Collection code
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
    <property name="dataSource" ref="dynamicDataSource" /> 
</bean> 
 
<bean id ="sqlBaseDAOProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> 
    <property name="transactionManager" ref="transactionManager" /> 
    <property name="target" ref="sqlBaseDAO" /> 
    <property name= "transactionAttributes">   
        <props> 
            <prop key="insert*">PROPAGATION_REQUIRED</prop> 
            <prop key="*">PROPAGATION_REQUIRED,readOnly</prop> 
        </props> 
    </property> 
</bean> 


Example of transaction management configuration when using Hibernate:
Xml code Collection code
<tx:annotation-driven transaction-manager= "transactionManager"/> 
 
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> 
    <property name="sessionFactory" ref="sessionFactory" /> 
</bean> 


    6. Dynamic data source Management control
How to choose and control the specific data source required in each business, but use manual control:
Java code Collection code
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");   
BaseDAO dao = (BaseDAO) context.getBean("sqlBaseDAO", BaseDAOImpl.class); 
 
try { 
    DBContextHolder.setCustomerType(DBContextHolder.DATA_SOURCE_FROM); 
    System.err.println(dao.select("select count(*) sum from TEST t ").get(0).get("SUM")); 
    DBContextHolder.setCustomerType(DBContextHolder.DATA_SOURCE_TO); 
    System.err.println(dao.select("select count(*) sum from TEST t ").get(0).get("SUM")); 
     
} catch (Exception e) { 
    e.printStackTrace(); 

也可以采用AOP的控制方式:
Java代码  收藏代码
@Aspect 
public class DynamicDataSourceAspect { 
    @Pointcut("execution (public service.impl..*.*(..))") 
    public void serviceExecution(){} 
     
    @Before("serviceExecution()") 
    public void setDynamicDataSource(JoinPoint jp) { 
        for(Object o : jp.getArgs()) { 
            //Process the specific logic and select the DataSource according to the specific situation CustomerContextHolder.setCustomerType() 
        } 
    } 



  7. Summary
   By extending Spring's AbstractRoutingDataSource, multiple data sources can be well implemented rout effect, and it has good scalability for expanding more data sources, just add data sources and modify the targetDataSources property configuration of DynamicDataSource. In the data source selection control, manual control can be used (when there is not much business logic), or a @Pointcut can be added to the entry of the Service using AOP's @Aspect, and the class content of JoinPoint can be judged in @Before. Select a specific data source.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326553867&siteId=291194637