Spring+mybatis動態(tài)切換數(shù)據(jù)源

<font size="1">版權聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載。https://blog.csdn.net/qq_36523638/article/details/84571304</font>

Spring+mybatis動態(tài)切換數(shù)據(jù)源

這是基于mybatis的動態(tài)切換數(shù)據(jù)源

數(shù)據(jù)源連接池使用的為Druid
bean工廠使用的是mybatis的SqlSessionFactoryBean

1、配置第一個數(shù)據(jù)源:

#配置第一個數(shù)據(jù)源(具體配置視情況而定):
<bean id="dataSourceA" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <!--<property name="filters" value="stat,log4j"/>-->
        <property name="maxActive" value="20"/>
        <property name="initialSize" value="1"/>
        <property name="maxWait" value="60000"/>
        <property name="minIdle" value="1"/>
        <property name="timeBetweenEvictionRunsMillis" value="3000"/>
        <property name="minEvictableIdleTimeMillis" value="300000"/>
        <property name="validationQuery" value="SELECT 'x' FROM DUAL"/>
        <property name="testWhileIdle" value="true"/>
        <property name="testOnBorrow" value="false"/>
        <property name="testOnReturn" value="false"/>
        <property name="poolPreparedStatements" value="true"/>
        <property name="maxPoolPreparedStatementPerConnectionSize" value="20"/>
    </bean>

2、配置第二個數(shù)據(jù)源:

#配置第二個數(shù)據(jù)源(具體配置視情況而定):
<bean id="dataSourceB" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
#配置第三個數(shù)據(jù)源
#配置第四個數(shù)據(jù)源
...
...
#可以配置多個數(shù)據(jù)源,只要不超出連接池的限制就可以

3、配置動態(tài)的數(shù)據(jù)源(關鍵)這里的開關:MultipleDataSource在第5步說明

 <bean id="multipleDataSource" class="包名.MultipleDataSource">
        <!-- 這里可以設置一個默認的數(shù)據(jù)源 >
        <property name="defaultTargetDataSource" ref="dataSource" />
        <property name="targetDataSources">
            <map>
                <entry key="dataSourceA" value-ref="dataSourceA" />
                <entry key="dataSourceB" value-ref="dataSourceB" />
            </map>
        </property>
    </bean>

4、然后在配置sqlSessionFactory的配置文件中添加屬性:

<property name="dataSource" ref="multipleDataSource"/>

添加完后是這樣:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="multipleDataSource"/>
        <一些插件的配置>
        ...
        </>
</bean>

5、包名.MultipleDataSource(動態(tài)切換開關):

public class MultipleDataSource extends AbstractRoutingDataSource {

    private static final ThreadLocal<String> dataSourceKey = new InheritableThreadLocal<String>();

    public static void setDataSourceKey(String dataSource) {
        dataSourceKey.set(dataSource);
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return dataSourceKey.get();
    }

    public static void removeDataSourceKey() {
        dataSourceKey.remove();
    }
}

6、最后自己寫一個切面AOP來做數(shù)據(jù)源的切換:

/**
 * 數(shù)據(jù)源切換AOP
 */
@Aspect
@Order(Ordered.LOWEST_PRECEDENCE-1)
@Component
public class MultipleDataSourceInterceptor {
    /**
     * 攔截器對所有的業(yè)務實現(xiàn)類請求之前進行數(shù)據(jù)源切換 特別注意,由于用到了多數(shù)據(jù)源,Mapper的調(diào)用最好只在*ServiceImpl,不然調(diào)用到非默認數(shù)據(jù)源的表時,會報表不存在的異常
     *
     * @throws
     */
    @Pointcut("execution(* com..*Impl.*(..)) || execution(* com..*Mapper.*(..))")
    public void log(){
    }
    @Before("log()")
    public void setDataSoruce(JoinPoint joinPoint)
            throws Throwable {
        //int i=8;
        //int j=i/0;
        Class<?> clazz = joinPoint.getTarget().getClass();
        String className = clazz.getName();
        if (ClassUtils.isAssignable(clazz, Proxy.class)) {
            className = joinPoint.getSignature().getDeclaringTypeName();
        }

        // 對包名含有serverwpms的設置為wpms數(shù)據(jù)源,否則默認為后臺的數(shù)據(jù)源
        if (className.contains(".serverwpms.")) {
                MultipleDataSource.setDataSourceKey("dataSourceA");
        }else if (className.contains(".server.") || className.contains(".mapper.")) {
                MultipleDataSource.setDataSourceKey("dataSourceB");
        }
    }

    /**
     * 當操作完成時,釋放當前的數(shù)據(jù)源 如果不釋放,頻繁點擊時會發(fā)生數(shù)據(jù)源沖突,本是另一個數(shù)據(jù)源的表,結(jié)果跑到另外一個數(shù)據(jù)源去,報表不存在
     *
     * @param joinPoint
     * @throws Throwable
     */
    @After("log()")
    public void removeDataSoruce(JoinPoint joinPoint)
            throws Throwable {
        MultipleDataSource.removeDataSourceKey();
    }
}

aop在springmvc的配置文件中定義下:

    <!-- 定義aspect類 -->
    <bean name="multipleDataSourceInterceptor" class="包名.MultipleDataSourceInterceptor"/>

<font size="4" color="red"> ----到這,系統(tǒng)中如果沒有用到事務,多數(shù)據(jù)源的配置就完成了,aop會根據(jù)你想要的需求去動態(tài)切換數(shù)據(jù)源,如果用到事務的話,僅僅到這是不夠的</font>

查閱資料說事務管理器是從connection級別來處理的,尤其是非常方便的DataSourceTransactionManager管理器,方法上用一個@Transactional注解就可以完成事務管理。
DataSourceTransactionManager管理器會對connection進行緩存(大概是這個意思),導致我們的aop MultipleDataSourceInterceptor雖然將數(shù)據(jù)源A的字符串切換為了數(shù)據(jù)源B,但是根本上的connection是并沒有切換回來的,這時候如果本該對數(shù)據(jù)源A操作的sql去操作數(shù)據(jù)源B了,會報表不存在的錯誤。

<font size = "4" color="blue">這時候應該怎么解決呢,其實主要是事務管理器執(zhí)行的優(yōu)先級比我們的aop優(yōu)先級高罷了,每次都是事務管理器先執(zhí)行,然后aop才去切換數(shù)據(jù)源,發(fā)現(xiàn)并沒有切換過來,手動設置下aop的優(yōu)先級高于事務管理器就好了,而事務管理器的優(yōu)先級默認為Order(1),也就是我之前所見過的最高的優(yōu)先級了</font>

<font size="5" color="green"></font>

上面<font size="5" color="red">@Order(Ordered.LOWEST_PRECEDENCE-1)</font>為關鍵,意思是給aop定一個優(yōu)先級,這個優(yōu)先級為多少呢, Ordered.LOWEST_PRECEDENCE為最低的order,大家都知道,Order越低,優(yōu)先級越高,事務管理器默認優(yōu)先級為1,這個aop的order比最低的Order還要小一個單位,代表優(yōu)先級是最高的。

注:事務管理器我是這么配置的:

<!-- 配置事務管理器,注意這里的dataSource和SqlSessionFactoryBean的dataSource要一致,不然事務就沒有作用了 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="multipleDataSource"/>
    </bean>

    <!-- 用注解來實現(xiàn)事務管理 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容