笔者之前论述过《
spring 数据源-AbstractRoutingDataSource 》
基于这个,我们只要保证,每次切换数据源,改变lookupkey就好了,具体思路就是创建一个AbstractRoutingDataSource
基于这个,我们只要保证,每次切换数据源,改变lookupkey就好了,具体思路就是创建一个AbstractRoutingDataSource
的子类,实现抽象方法determineCurrentLookupKey,每次修改数据源,就是让determineCurrentLookupKey返回不同的lookupkey就好了。
aop拦截service服务里头的方法,切换数据源。通常,我们平常都是通过业务来对库表进行分库,那么service就可以根据业务来划分目录,不同目录下的service服务方法,aop拦截切入点,可以根据目录来设定不同的数据源。
下面附上代码:
DynamicDataSource.java(实现determineCurrentLookupKey方法)
package com.wlt.core.common.dynamicdb;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DbContextHolder.getDbType();
}
}
DBTypeEnum(枚举所有数据源的key)
package com.wlt.core.common.dynamicdb;
public enum DBTypeEnum {
/**
* 测试库
*/
TEST("test");
private String value;
DBTypeEnum(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
DbContextHolder(当前线程数据源持有对象)
package com.wlt.core.common.dynamicdb;
public class DbContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
/**
* 设置数据源
* @param dbTypeEnum
*/
public static void setDbType(DBTypeEnum dbTypeEnum) {
contextHolder.set(dbTypeEnum.getValue());
}
/**
* 取得当前数据源
* @return
*/
public static String getDbType() {
return contextHolder.get();
}
/**
* 清除上下文数据
*/
public static void clearDbType() {
contextHolder.remove();
}
}
DataSourceInterceptor(基于aop的数据源切换)
package com.wlt.core.common.dynamicdb;
import org.aspectj.lang.JoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DataSourceInterceptor {
private static final Logger logger = LoggerFactory.getLogger(DataSourceInterceptor.class);
public void setTest() {
DbContextHolder.setDbType(DBTypeEnum.TEST);
}
public void setDb(JoinPoint jp) {
String packageName = jp.getTarget().getClass().getPackage().getName();
if (packageName.contains(".test.")) {
DbContextHolder.setDbType(DBTypeEnum.TEST);
}else {
//设置默认数据源
DbContextHolder.setDbType(DBTypeEnum.TEST);
}
logger.debug("当前数据源:"+DbContextHolder.getDbType());
}
}
xml配置
maven依赖包配置:
<properties>
<druid.version>1.1.8</druid.version>
</properties>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
xml配置:
<?mybatisxml 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"
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.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
">
<!-- 启用CGliB -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!-- 测试库 数据源-->
<bean name="test" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"
destroy-method="close">
<property name="url" value="jdbc:mysql://127.0.0.1:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
<!-- 初始化连接大小 -->
<property name="initialSize" value="0"/>
<!-- 连接池最大使用连接数量 -->
<property name="maxActive" value="5"/>
<!-- 连接池最小空闲 -->
<property name="minIdle" value="0"/>
<!-- 获取连接最大等待时间 -->
<property name="maxWait" value="60000"/>
<property name="validationQuery" value="SELECT 1 FROM DUAL"/>
<property name="testOnBorrow" value="false"/>
<property name="testOnReturn" value="false"/>
<property name="testWhileIdle" value="true"/>
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="60000"/>
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="25200000"/>
<property name="removeAbandoned" value="true"/>
<property name="removeAbandonedTimeout" value="1800"/>
<!-- 关闭abanded连接时输出错误日志 -->
<property name="logAbandoned" value="true"/>
<!-- 监控数据库 -->
<property name="filters" value="mergeStat"/>
</bean>
<bean id="dataSource" class="com.wlt.core.common.dynamicdb.DynamicDataSource">
<property name="defaultTargetDataSource" ref="test"/>
<property name="targetDataSources">
<map key-type="java.lang.String">
<!--key="test",key就是lookupkey-->
<entry key="test" value-ref="test"/>
<!--可以配置多个数据源-->
<!--<entry key="money" value-ref="money"/>-->
</map>
</property>
</bean>
<!-- Spring整合Mybatis -->
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- 自动扫描Mapping.xml文件 -->
<property name="mapperLocations" value="classpath*:mybatisxml/**/*Mapper.xml"/>
<property name="configLocation" value="classpath:mybatisxml/mybatis-config.xml"/>
<property name="typeAliasesPackage" value="com.wlt.biz.entity"/>
<property name="plugins">
<array>
<!-- 分页插件配置 -->
<bean id="paginationInterceptor" class="com.baomidou.mybatisplus.plugins.PaginationInterceptor">
<!--
MYSQL->`mysql`
ORACLE->`oracle`
DB2->`db2`
H2->`h2`
HSQL->`hsql`
SQLITE->`sqlite`
POSTGRE->`postgresql`
SQLSERVER2005->`sqlserver2005`
SQLSERVER->`sqlserver`
-->
<property name="dialectType" value="mysql"/>
</bean>
</array>
</property>
<!-- MP 全局配置注入 -->
<property name="globalConfig" ref="globalConfig"/>
</bean>
<bean id="globalConfig" class="com.baomidou.mybatisplus.entity.GlobalConfiguration">
<!--
AUTO->`0`("数据库ID自增")QW
INPUT->`1`(用户输入ID")
ID_WORKER->`2`("全局唯一ID")
UUID->`3`("全局唯一ID")
-->
<property name="idType" value="3"/>
</bean>
<!-- MyBatis 动态实现 -->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 对Dao 接口动态实现,需要知道接口在哪 -->
<property name="basePackage" value="com.wlt.biz.mapper"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 启用支持Annotation注解方式的事务管理 -->
<tx:annotation-driven transaction-manager="transactionManager" order="2"/>
<!--aop 换源-->
<bean id="dataSourceInterceptor"
class="com.wlt.core.common.dynamicdb.DataSourceInterceptor"/>
<aop:config>
<aop:aspect ref="dataSourceInterceptor" order="0">
<aop:pointcut id="testpoint"
expression="execution(* com.wlt.biz.service.test..*.*(..))"/>
<aop:before method="setTest" pointcut-ref="testpoint"/>
</aop:aspect>
<aop:aspect ref="dataSourceInterceptor" order="0">
<aop:pointcut id="dbAll" expression="execution(* com.baomidou.mybatisplus.service..*.*(..))"/>
<aop:before method="setDb" pointcut-ref="dbAll"/>
</aop:aspect>
</aop:config>
</beans>