How to configure multiple data sources and switch between them?

Keywords: Java JDBC Database Spring Mybatis

Recently, a project of the company needs to connect two databases (A and b) for operation. Some modules query database a, and some modules query database B. therefore, the project background uses the spring MVC + mybatis + MySQL architecture. After two days of tossing, it is finally completed. Here, the modification process is recorded.

Usage scenarios

The scenarios for using multiple data sources are as follows:

  1. Master-slave database switching
  2. Read / write separation
  3. Compatible with old Libraries

Realization principle

In the version of spring 2. X, the Proxy mode is used to implement a virtual data source in the scheme, and use it to encapsulate the data source selection logic, so that the data source selection logic can be effectively separated from the Client. The Client provides the context needed for selection, and the virtual dynamic datasource implements the selection of data source according to the context provided by the Client.

The specific implementation is that the virtual dynamic DataSource only needs to inherit AbstractRoutingDataSource to implement determineCurrentLookupKey(), which returns the key value of the DataSource to be used, and then takes the corresponding DataSource from the map of resolvedDataSources according to the key value. If it cannot be found, the default resolvedDefaultDataSource is used.

Detailed implementation process

  1. Modify spring's configuration file
    Multiple data sources need to be configured in the configuration file. After transformation, the main configuration is as follows:
<bean id="dataSourceTargetA" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close" scope="singleton">
        <property name="driverClass" value="${jdbc.a.driverClassName}" />
        <property name="jdbcUrl" value="${jdbc.a.url}" />
        <property name="user" value="${jdbc.a.username}" />
        <property name="password" value="${jdbc.a.password}" />
        <!--When the connections in the connection pool are exhausted c3p0 The number of connections acquired at one time. Default: 3 -->
        <property name="acquireIncrement" value="${c3p0.acquireIncrement}"></property>
        <!--Obtain three connections during initialization. The value should be minPoolSize And maxPoolSize Between. Default: 3 -->
        <property name="initialPoolSize" value="${c3p0.initialPoolSize}"></property>
        <!--Maximum idle time,60 If not used within seconds, the connection is discarded. If it is 0, it will never be discarded. Default: 0 -->
        <property name="maxIdleTime" value="${c3p0.maxIdleTime}"></property>
        <!--The maximum number of connections remaining in the connection pool. Default: 15 -->
        <property name="maxPoolSize" value="${c3p0.maxPoolSize}"></property>
        <property name="minPoolSize" value="${c3p0.minPoolSize}"></property>
        <!--The interval between two connections, in milliseconds. Default: 1000 -->
        <property name="acquireRetryDelay" value="1000"></property>
        <!--Defines the number of times a new connection from the database has failed to be retried. Default: 30 -->
        <property name="acquireRetryAttempts" value="60"></property>
        <!--Failure to get the connection will cause all threads waiting for the connection pool to get the connection to throw an exception. But the data source is still valid and will be called next time getConnection()Continue trying to get a connection. If set to true,The data source will declare that it is disconnected and permanently shut down after an attempt to get a connection fails. Default:
            false -->
        <property name="breakAfterAcquireFailure" value="false"></property>
    </bean>

    <bean id="dataSourceTargetB" class="com.mchange.v2.c3p0.ComboPooledDataSource"
          destroy-method="close" scope="singleton">
        <property name="driverClass" value="${jdbc.b.driverClassName}" />
        <property name="jdbcUrl" value="${jdbc.b.url}" />
        <property name="user" value="${jdbc.b.username}" />
        <property name="password" value="${jdbc.b.password}" />
        <!--When the connections in the connection pool are exhausted c3p0 The number of connections acquired at one time. Default: 3 -->
        <property name="acquireIncrement" value="${c3p0.acquireIncrement}"></property>
        <!--Obtain three connections during initialization. The value should be minPoolSize And maxPoolSize Between. Default: 3 -->
        <property name="initialPoolSize" value="${c3p0.initialPoolSize}"></property>
        <!--Maximum idle time,60 If not used within seconds, the connection is discarded. If it is 0, it will never be discarded. Default: 0 -->
        <property name="maxIdleTime" value="${c3p0.maxIdleTime}"></property>
        <!--The maximum number of connections remaining in the connection pool. Default: 15 -->
        <property name="maxPoolSize" value="${c3p0.maxPoolSize}"></property>
        <property name="minPoolSize" value="${c3p0.minPoolSize}"></property>
        <!--The interval between two connections, in milliseconds. Default: 1000 -->
        <property name="acquireRetryDelay" value="1000"></property>
        <!--Defines the number of times a new connection has failed to get from the database. Default: 30 -->
        <property name="acquireRetryAttempts" value="60"></property>
        <!--Failure to get the connection will cause all threads waiting for the connection pool to get the connection to throw an exception. But the data source is still valid and will be called next time getConnection()Continue trying to get a connection. If set to true,The data source will declare that it is disconnected and permanently shut down after an attempt to get a connection fails. Default:
            false -->
        <property name="breakAfterAcquireFailure" value="false"></property>
    </bean>
    <!-- Dynamic data source -->
    <bean id="dynamicDataSource" class="com.test.util.DynamicDataSource" >
        <property name="targetDataSources">
            <map key-type="java.lang.String">
                <entry value-ref="dataSourceTargetA" key="dataSourceTargetA"></entry>
                <entry value-ref="dataSourceTargetB" key="dataSourceTargetB"></entry>
            </map>
        </property>
        <property name="defaultTargetDataSource" ref="dataSourceTargetA" >
        </property>
    </bean>


    <bean id="txManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dynamicDataSource" />
    </bean>


    <!-- MyBatis ORM operation class -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dynamicDataSource" />
        <property name="mapperLocations">
            <list>
                <value>classpath*:com/test/**/*Mapper.xml</value>
            </list>
        </property>
        <property name="configLocation" value="classpath:provider-sql-map-config.xml" />
    </bean>

Posted by tryin_to_learn on Sun, 01 Dec 2019 07:40:39 -0800