多数据源
 

spring-database.xml


<?xml version="1.0" encoding="UTF-8"?>

<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"

       xmlns:context="http://www.springframework.org/schema/context"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

       http://www.springframework.org/schema/beans/spring-beans.xsd

       http://www.springframework.org/schema/context 

       http://www.springframework.org/schema/context/spring-context.xsd

       http://www.springframework.org/schema/tx

                     http://www.springframework.org/schema/tx/spring-tx.xsd

                     http://www.springframework.org/schema/aop

                     http://www.springframework.org/schema/aop/spring-aop.xsd">


<!--开启自动代理功能 true使用CGLIB   -->

<aop:aspectj-autoproxy proxy-target-class="true"/>

<!-- 声明AOP 切换数据源通知 类中加@Component 自动扫描xml中不用配<bean>了

<bean id="dataSourceAdvice" class="com.xxx.utils.db.DataSourceAdvice" />

-->

<!-- 配置通知和切点 注意这个一定要配置在事务声明(txAdvice)之前 否则就会出现数据源切换出错  -->

<aop:config>

    <aop:advisor pointcut="execution(* com.xxx.service.*.*(..))" advice-ref="dataSourceAdvice" />

</aop:config>


    <bean id="parentDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"

          destroy-method="close">

        <property name="driverClassName" value="$[jdbc-driver]"/>

        <property name="url" value="${jdbc-url-global}"/>

        <property name="username" value="${jdbc-user-global}"/>

        <property name="password" value="${jdbc-password-global}"/>

        <property name="filters" value="stat"/>

        <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'"/>

        <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"/>

        <property name="connectionInitSqls" value="set names utf8mb4;"/>

    </bean>


    <bean id="ds001" parent="parentDataSource">

        <property name="url" value="$[jdbc-url-001]"/>

        <property name="username" value="$[jdbc-user-001]"/>

        <property name="password" value="$[jdbc-password-001]"/>

    </bean>

    

    <bean id="ds002" parent="parentDataSource">

        <property name="url" value="$[jdbc-url-002]"/>

        <property name="username" value="$[jdbc-user-002]"/>

        <property name="password" value="$[jdbc-password-002]"/>

    </bean>

    

    <bean id="ds003" parent="parentDataSource">

        <property name="url" value="$[jdbc-url-003]"/>

        <property name="username" value="$[jdbc-user-003]"/>

        <property name="password" value="$[jdbc-password-003]"/>

    </bean>

    

    <!-- config switch routing db -->

    <bean id="dataSource" class="com.xxx.utils.db.RoutingDataSource">

        <property name="targetDataSources">

            <map key-type="java.lang.String">

                <entry key="ds001" value-ref="ds001"/>

                <entry key="ds002" value-ref="ds002"/>

                <entry key="ds003" value-ref="ds003"/>

            </map>

        </property>

<property name="defaultTargetDataSource" ref="ds001" />

    </bean>

    <!-- 配置sqlSessionTemplate -->

<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">

<constructor-arg index="0" ref="sqlSessionFactory" />

</bean>

    <!--事务-->

    <bean id="xxxTransactionManager"

          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

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

    </bean>


    <bean id="btTransactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">

        <property name="transactionManager" ref="xxxTransactionManager"></property>

        <property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"></property>

    </bean>

    

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">

        <property name="configLocation" value="classpath:mybatis-config.xml"></property>

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

        <property name="mapperLocations">

            <array>

                <value>classpath:mapper/*Mapper.xml</value>

            </array>

        </property>

    </bean>

<!-- 扫描路径配置,可以多个包名,中间逗号分割 -->

    <bean id="scannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">

        <property name="basePackage" value="com.xxx.dao"/>

        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>

    </bean>

    

    <aop:config>

<aop:pointcut id="managerPointcut"

expression="execution(* com.xxx.service.impl.*.*(..))" />

<aop:advisor advice-ref="txAdvice" pointcut-ref="managerPointcut" />

</aop:config>


<tx:advice id="txAdvice" transaction-manager="xxxTransactionManager">

<tx:attributes>

<tx:method name="get*" propagation="SUPPORTS" read-only="true" />

<tx:method name="find*" propagation="SUPPORTS" read-only="true" />

<tx:method name="search*" propagation="SUPPORTS" read-only="true" />

<tx:method name="list*" propagation="SUPPORTS" read-only="true" />

<tx:method name="is*" propagation="SUPPORTS" read-only="true" />

<tx:method name="has*" propagation="SUPPORTS" read-only="true" />

<tx:method name="*" propagation="REQUIRED" read-only="false" rollback-for="Exception" />

</tx:attributes>

</tx:advice>


</beans>


---------------------------------------------------------------

DataSourceAdvice.java


package com.xxx.utils.db;


import java.lang.reflect.Method;


import lombok.extern.slf4j.Slf4j;


import org.aspectj.lang.annotation.Aspect;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.aop.AfterReturningAdvice;

import org.springframework.aop.MethodBeforeAdvice;

import org.springframework.aop.ThrowsAdvice;

import org.springframework.stereotype.Component;


/**

 * 

 * <b>数据源切面</b><br/>

 * 

 * @author Seal

 */

@Slf4j

@Aspect

@Component

public class DataSourceAdvice implements MethodBeforeAdvice, AfterReturningAdvice, ThrowsAdvice {


private final Logger logger = LoggerFactory.getLogger(this.getClass());

// service方法执行之前被调用

public void before(Method method, Object[] args, Object target) throws Throwable {

logger.info("切入点: " + target.getClass().getName() + " 类中 " + method.getName() + " 方法");

if (method.getName().startsWith("insert") || method.getName().startsWith("create")

         || method.getName().startsWith("save") || method.getName().startsWith("edit")

         || method.getName().startsWith("update") || method.getName().startsWith("delete")

         || method.getName().startsWith("remove") || method.getName().startsWith("invalid")) {

DBContext.setDBKey((String)args[0]);

logger.info("Change DB: " + DBContext.getDBKey());

}

}


// service方法执行完之后被调用

public void afterReturning(Object var1, Method var2, Object[] var3, Object var4) throws Throwable {

logger.info("If need change db, call method.");

}


// 抛出Exception之后被调用

public void afterThrowing(Method method, Object[] args, Object target, Exception ex) throws Throwable {

logger.info("Method throw exception");

}


}


-------------------------------------------------------------------

DBContext.java


package com.xxx.utils.db;


/**

 * 

 * @author Seal

 */

public class DBContext {


// define count of database and it must match with

// resources/properties/jdbc.properties

private static final int DB_COUNT = 3;


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


private static final String DB_001 = "ds001";

private static final String DB_002 = "ds002";

private static final String DB_003 = "ds003";


public static String getDBKey() {

return tlDbKey.get();

}


public static void setDBKey(String dbKey) {

tlDbKey.set(dbKey);

}

public static int getDbCount() {

return DB_COUNT;

}


public static String getDb001() {

return DB_001;

}


public static String getDb002() {

return DB_002;

}


public static String getDb003() {

return DB_003;

}


}


-------------------------------------------------------------------

DataSourceAdvice.java


package com.xxx.utils.db;


import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

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


/**

 * 

 * @author Seal

 */

public class RoutingDataSource extends AbstractRoutingDataSource {

protected final Logger logger = LoggerFactory.getLogger(this.getClass());


@Override

protected Object determineCurrentLookupKey() {

logger.info("---------切换数据源-----------" + DBContext.getDBKey() + "==========");

return DBContext.getDBKey();

}


}