AbstractRoutingDataSource 實現動態切換數據源


擴展AbstractRoutingDataSource類

package com.datasource.test.util.database;
 
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
 
/**
 * 獲取數據源(依賴於spring)
 */
public class DynamicDataSource extends AbstractRoutingDataSource{
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceHolder.getDataSource();
    }
}

DataSourceHolder這個類則是我們自己封裝的對數據源進行操作的類:

package com.datasource.test.util.database;
 
/**
 * 數據源操作
 */
public class DataSourceHolder {
    //線程本地環境
    private static final ThreadLocal<String> dataSources = new ThreadLocal<String>();
    //設置數據源
    public static void setDataSource(String customerType) {
        dataSources.set(customerType);
    }
    //獲取數據源
    public static String getDataSource() {
        return (String) dataSources.get();
    }
    //清除數據源
    public static void clearDataSource() {
        dataSources.remove();
    }
 
}

在注解中使用

@DataSource(name=DataSource.slave1)
public List getProducts(){}

注解定義:

package com.datasource.test.util.database;
 
import java.lang.annotation.*;
 
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
    String name() default DataSource.master;
 
    public static String master = "dataSource1";
 
    public static String slave1 = "dataSource2";
 
    public static String slave2 = "dataSource3";
 
}

 

定義攔截器,攔截DataSource的值

public class DataSourceAspect {

    /**
     * 攔截目標方法,獲取由@DataSource指定的數據源標識,設置到線程存儲中以便切換數據源
     * 
     * @param point
     * @throws Exception
     */
    public void intercept(JoinPoint point) throws Exception {
        Class<?> target = point.getTarget().getClass();
        MethodSignature signature = (MethodSignature) point.getSignature();
        // 默認使用目標類型的注解,如果沒有則使用其實現接口的注解
        for (Class<?> clazz : target.getInterfaces()) {
            resolveDataSource(clazz, signature.getMethod());
        }
        resolveDataSource(target, signature.getMethod());
    }

    /**
     * 提取目標對象方法注解和類型注解中的數據源標識
     * 
     * @param clazz
     * @param method
     */
    private void resolveDataSource(Class<?> clazz, Method method) {
        try {
            Class<?>[] types = method.getParameterTypes();
            // 默認使用類型注解
            if (clazz.isAnnotationPresent(DataSource.class)) {
                DataSource source = clazz.getAnnotation(DataSource.class);
                DynamicDataSourceHolder.setDataSource(source.value());
            }
            // 方法注解可以覆蓋類型注解
            Method m = clazz.getMethod(method.getName(), types);
            if (m != null && m.isAnnotationPresent(DataSource.class)) {
                DataSource source = m.getAnnotation(DataSource.class);
                DynamicDataSourceHolder.setDataSource(source.value());
            }
        } catch (Exception e) {
            System.out.println(clazz + ":" + e.getMessage());
        }
    }

}

spring 中配置攔截器:

<bean id="dataSourceAspect" class="com.test.context.datasource.DataSourceAspect" />
    <aop:config>
        <aop:aspect ref="dataSourceAspect">
            <!-- 攔截所有service方法 -->
            <aop:pointcut id="dataSourcePointcut" expression="execution(* com.test.*.dao.*.*(..))"/>
            <aop:before pointcut-ref="dataSourcePointcut" method="intercept" />
        </aop:aspect>
    </aop:config>
</bean>

多數據源配置:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Spring 數據庫相關配置 放在這里 -->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
 
    <bean id = "dataSource1" class = "com.mysql.jdbc.jdbc2.optional.MysqlDataSource">   
        <property name="url" value="${db1.url}"/>
        <property name = "user" value = "${db1.user}"/>
        <property name = "password" value = "${db1.pwd}"/>
        <property name="autoReconnect" value="true"/>
        <property name="useUnicode"  value="true"/>
        <property name="characterEncoding" value="UTF-8"/>
    </bean>
 
    <bean id = "dataSource2" class = "com.mysql.jdbc.jdbc2.optional.MysqlDataSource">
        <property name="url" value="${db2.url}"/>
        <property name = "user" value = "${db2.user}"/>
        <property name = "password" value = "${db2.pwd}"/>
        <property name="autoReconnect" value="true"/>
        <property name="useUnicode"  value="true"/>
        <property name="characterEncoding" value="UTF-8"/>
    </bean>
 
    <bean id = "dataSource3" class = "com.mysql.jdbc.jdbc2.optional.MysqlDataSource">
        <property name="url" value="${db3.url}"/>
        <property name = "user" value = "${db3.user}"/>
        <property name = "password" value = "${db3.pwd}"/>
        <property name="autoReconnect" value="true"/>
        <property name="useUnicode"  value="true"/>
        <property name="characterEncoding" value="UTF-8"/>
    </bean>
    <!-- 配置多數據源映射關系 -->
    <bean id="dataSource" class="com.datasource.test.util.database.DynamicDataSource">
        <property name="targetDataSources">
            <map key-type="java.lang.String">
        <entry key="dataSource1" value-ref="dataSource1"></entry>
                <entry key="dataSource2" value-ref="dataSource2"></entry>
                <entry key="dataSource3" value-ref="dataSource3"></entry>
            </map>
        </property>
    <!-- 默認目標數據源為你主庫數據源 -->
        <property name="defaultTargetDataSource" ref="dataSource1"/>
    </bean>
 
    <bean id="sessionFactoryHibernate" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">com.datasource.test.util.database.ExtendedMySQLDialect</prop>
                <prop key="hibernate.show_sql">${SHOWSQL}</prop>
                <prop key="hibernate.format_sql">${SHOWSQL}</prop>
                <prop key="query.factory_class">org.hibernate.hql.classic.ClassicQueryTranslatorFactory</prop>
                <prop key="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</prop>
                <prop key="hibernate.c3p0.max_size">30</prop>
                <prop key="hibernate.c3p0.min_size">5</prop>
                <prop key="hibernate.c3p0.timeout">120</prop>
                <prop key="hibernate.c3p0.idle_test_period">120</prop>
                <prop key="hibernate.c3p0.acquire_increment">2</prop>
                <prop key="hibernate.c3p0.validate">true</prop>
                <prop key="hibernate.c3p0.max_statements">100</prop>
            </props>
        </property>
    </bean>
 
    <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
        <property name="sessionFactory" ref="sessionFactoryHibernate"/>
    </bean>
 
    <bean id="dataSourceExchange" class="com.datasource.test.util.database.DataSourceExchange"/>
 
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactoryHibernate"/>
    </bean>
 
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="insert*" propagation="NESTED" rollback-for="Exception"/>
            <tx:method name="add*" propagation="NESTED" rollback-for="Exception"/>
            <tx:method name="update*" propagation="NESTED" rollback-for="Exception"/>
            <tx:method name="modify*" propagation="NESTED" rollback-for="Exception"/>
            <tx:method name="edit*" propagation="NESTED" rollback-for="Exception"/>
            <tx:method name="del*" propagation="NESTED" rollback-for="Exception"/>
            <tx:method name="save*" propagation="NESTED" rollback-for="Exception"/>
            <tx:method name="send*" propagation="NESTED" rollback-for="Exception"/>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="find*" read-only="true"/>
            <tx:method name="query*" read-only="true"/>
            <tx:method name="search*" read-only="true"/>
            <tx:method name="select*" read-only="true"/>
            <tx:method name="count*" read-only="true"/>
        </tx:attributes>
    </tx:advice>
 
    <aop:config>
        <aop:pointcut id="service" expression="execution(* com.datasource..*.service.*.*(..))"/>
        <!-- 關鍵配置,切換數據源一定要比持久層代碼更先執行(事務也算持久層代碼) -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="service" order="2"/>
        <aop:advisor advice-ref="dataSourceExchange" pointcut-ref="service" order="1"/>
    </aop:config>
 
</beans>

 

PS: 以上是從兩個地址復制的代碼,不作准確性驗證,但思路就是這樣。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM