基於註解的Spring多資料來源配置和使用(非事務)

不知為何就叫呵呵發表於2017-03-20

原文:基於註解的Spring多資料來源配置和使用

 

1。建立DynamicDataSource類,繼承AbstractRoutingDataSource

package com.rps.dataSource;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceHolder.getDataSource();
    }

}

  建立DynamicDataSourceHolder類

package com.rps.dataSource;

public class DynamicDataSourceHolder {
    /**
     * 注意:資料來源標識儲存線上程變數中,避免多執行緒運算元據源時互相干擾
     */
    private static final ThreadLocal<String> THREAD_DATA_SOURCE = new ThreadLocal<String>();

    public static String getDataSource() {
        return THREAD_DATA_SOURCE.get();
    }

    public static void setDataSource(String dataSource) {
        THREAD_DATA_SOURCE.set(dataSource);
    }

    public static void clearDataSource() {
        THREAD_DATA_SOURCE.remove();
    }
}

2.配置多資料來源

<util:properties id="jdbc"
        location="classpath:etc/mybatis/db.properties" />

    <!-- 連線池配置開始 -->
    <!-- Druid連線池 -->
    <bean id="druidDataSourceAccount" class="com.alibaba.druid.pool.DruidDataSource"
        destroy-method="close" lazy-init="true">
        <property name="driverClassName" value="#{jdbc.driverClassName}" />
        <property name="url" value="#{jdbc.account_url}" />
        <property name="username" value="#{jdbc.username}" />
        <property name="password" value="#{jdbc.password}" />
    </bean>
    <bean id="druidDataSourceCommon" class="com.alibaba.druid.pool.DruidDataSource"
        destroy-method="close" lazy-init="true">
        <property name="driverClassName" value="#{jdbc.driverClassName}" />
        <property name="url" value="#{jdbc.common_url}" />
        <property name="username" value="#{jdbc.username}" />
        <property name="password" value="#{jdbc.password}" />
    </bean>
    <bean id="druidDataSourceData" class="com.alibaba.druid.pool.DruidDataSource"
        destroy-method="close" lazy-init="true">
        <property name="driverClassName" value="#{jdbc.driverClassName}" />
        <property name="url" value="#{jdbc.data_url}" />
        <property name="username" value="#{jdbc.username}" />
        <property name="password" value="#{jdbc.password}" />
    </bean>

    <!-- 連線池配置結束 -->

    <!-- MyBatis整合開始 -->
    <bean id="dynamicDataSource" class="com.rps.dataSource.DynamicDataSource">
        <property name="targetDataSources">
            <map key-type="java.lang.String">
                <entry key="account" value-ref="druidDataSourceAccount"></entry>
                <entry key="common" value-ref="druidDataSourceCommon"></entry>
                <entry key="data" value-ref="druidDataSourceData"></entry>
            </map>
        </property>
    </bean>

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> 
        <property name="dataSource" ref="dynamicDataSource"/>
         <property name="mapperLocations" value="classpath:com/rps/**/*.xml"/> 
         <property name="configLocation" value="classpath:etc/mybatis/mybatis-config.xml"/> 
    </bean> 


    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.rps" />
        <property name="annotationClass" value="com.rps.annotations.MyBatisRepository" />
    </bean>
    <!-- MyBatis整合結束 -->


    <!-- 配置資料庫事務開始 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
        <property name="dataSource" ref="dynamicDataSource"/> 
    </bean>
    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
    <!-- 配置資料庫事務結束 -->

3.在使用資料來源前,選擇資料來源:

  

DynamicDataSourceHolder.setDataSource("account");

  或:使用spring aop 動態切換:

package com.rps.aspect;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import com.rps.annotations.DataSource;
import com.rps.dataSource.DynamicDataSourceHolder;

@Component
@Aspect
public class DataSourceAspect {
    /**
     * 攔截目標方法,獲取由@DataSource指定的資料來源標識,設定到執行緒儲存中以便切換資料來源
     * 
     * @param point
     * @throws Exception
     */
    @Before("execution(* com.rps.*.model.dao.*.*(..))")
    public void intercept(JoinPoint point) throws Exception {
        System.out.println("*****************************");
        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((Class<? extends Annotation>) 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());
        }
    }
}

注:事務管理配置一定要配置在往DynamicDataSourceHolder 中注入資料來源key之前 ,否則會報 Could not open JDBC Connection for transaction; nested exception is java.lang.IllegalStateException: Cannot determine target DataSource for lookup key [null] 找不到資料來源錯誤

相關文章