多數據源切換配合事務問題


用DynamicDataSource類輔助切換多數據源

實現AbstractRoutingDataSource接口,在類中定義

public static final String plateBaseDataSource= "plateBaseDataSource";
public static final String lgCommonDataSource= "lgCommonDataSource";
public static final String subApplicationDataSource= "subApplicationDataSource";
public static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(){};

切換與清空數據緣方法為

    public static void clearCustomerDataSource(){
        contextHolder.remove();
    }
    
    public static void setCustomerDataSource(String customerType){
        contextHolder.set(customerType);
    }

 

對應配置文件中,數據源定義為

    <bean id="plateBaseDataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" value="${plateBase.driverClassName}" />
        <property name="url" value="${plateBase.url}" />
        <property name="username" value="${plateBase.uname}" />
        <property name="password" value="${plateBase.password}" />
        <!-- 連接池啟動時的初始值 -->
        <property name="initialSize" value="${plateBase.initialSize}" />
        <!-- 連接池的最大值 -->
        <property name="maxActive" value="${plateBase.maxActive}" />
        <!-- 最大空閑值.當經過一個高峰時間后,連接池可以慢慢將已經用不到的連接慢慢釋放一部分,一直減少到maxIdle為止 -->
        <property name="maxIdle" value="${plateBase.maxIdle}" />
        <!-- 最小空閑值.當空閑的連接數少於閥值時,連接池就會預申請去一些連接,以免洪峰來時來不及申請 -->
        <property name="minIdle" value="${plateBase.minIdle}" />
        <property name="validationQuery" value="select 1 from dual"></property>
    </bean>
    
    <bean id="lgCommonDataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" value="${lgCommon.driverClassName}" />
        <property name="url" value="${lgCommon.url}" />
        <property name="username" value="${lgCommon.uname}" />
        <property name="password" value="${lgCommon.password}" />
        <!-- 連接池啟動時的初始值 -->
        <property name="initialSize" value="${lgCommon.initialSize}" />
        <!-- 連接池的最大值 -->
        <property name="maxActive" value="${lgCommon.maxActive}" />
        <!-- 最大空閑值.當經過一個高峰時間后,連接池可以慢慢將已經用不到的連接慢慢釋放一部分,一直減少到maxIdle為止 -->
        <property name="maxIdle" value="${lgCommon.maxIdle}" />
        <!-- 最小空閑值.當空閑的連接數少於閥值時,連接池就會預申請去一些連接,以免洪峰來時來不及申請 -->
        <property name="minIdle" value="${lgCommon.minIdle}" />
        <property name="validationQuery" value="select 1 from dual"></property>
    </bean>

定義DataSource為

    <bean id="dataSource" class="heb.ysbs.utils.lgcommon.DynamicDataSource">
        <property name="defaultTargetDataSource" ref="${defaultTargetDataSource}"/>
         <property name="customDefaultDataSource" value="${defaultTargetDataSource}"/>
         <property name="customLgBaseLogTableName" value="${customLgBaseLogTableName}"/>
        <property name="targetDataSources" >
            <map>
                <entry key="plateBaseDataSource" value-ref="plateBaseDataSource"/>
                <entry key="lgCommonDataSource" value-ref="lgCommonDataSource"/>
            </map>
        </property>
    </bean>

jdbctemplate常規定義,沒什么好講

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource" />
    </bean>

以上為數據源切換部分 ,這時切換數據源可直接調用DynamicDataSource.clearCustomerDataSource,在配置事務后,進入事務線程后jdbc得到默認DataSource,DynamicDataSource方法設置數據源在事務中執行的jdbc中不體現(線程問題,一知半解)

解決方法是在事務開啟前切換數據源,用aop增強

定義工具類

<bean id="dataSourceExchange" class="fasp.utils.lgcommon.DataSourceExchange"></bean>

定義切片,排序在事務之前 :事務切點order=2,

    <aop:config proxy-target-class="true">
        <aop:pointcut id="dataSourcetransactionPointcut" expression="(execution(* fasp.service.lgcommon..*.*(..))) )" />
        <aop:aspect id="aspect" ref="dataSourceExchange" order="1" >
            <aop:before method="before" pointcut-ref="dataSourcetransactionPointcut" /> 
             <aop:after method="after" pointcut-ref="dataSourcetransactionPointcut" /> 
        </aop:aspect>
    </aop:config> 

個人習慣,感覺定義兩個切點靈活,這里沒有必要每個方法都進行數據切換如果也用 在一個aop:pointcut 定義兩個aop:advisor就還要在攔截器中判斷調用方法,其他資料中看到的

invoke方法

package com.lei.demo.aop.schema;

import java.lang.reflect.Method;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class HelloAroundAdvice implements MethodInterceptor {

    public Object invoke(MethodInvocation arg0) throws Throwable {

        System.out.println("++++++before advice");
        arg0.proceed();
        System.out.println("++++++after advice");
        
        return null;
    }

}

工具類DataSourceExchange中方法

 1 /**
 2      * 事務加載前執行
 3      * 切換數據源
 4      * @param call
 5      */
 6     public void before(JoinPoint call){
 7 
 8         Object[] obj=call.getArgs();//參數集合
 9         String dataSourceName=(String)(obj[0]);//規則:默認第一個參數為datasourcename
10         //Signature signature=call.getSignature(); //參數類型集合
11         //    String [] argNames= ( (MethodSignature) signature ).getParameterNames();//參數名
12         DynamicDataSource.setCustomerDataSource( dataSourceName);
13 
14     }
15     /**
16      * 事務加載前執行
17      * 清空數據源
18      * @param call
19      */
20     public void after(JoinPoint call){
21         DynamicDataSource.clearCustomerDataSource();
22         //String parameter= DynamicDataSource.getCustomDefaultDataSource();
23         //DynamicDataSource.setCustomerDataSource(parameter);
24     }
25 }
View Code

 


免責聲明!

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



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