干货,复制下来直接用,亲测
配置文件
#master
spring.datasource.master.url=jdbc:mysql://127.0.0.1:3306/anyikongku?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false&allowMultiQueries=true&autoReconnect=true&allowPublicKeyRetrieval=true
spring.datasource.master.username=root
spring.datasource.master.password=sql
spring.datasource.master.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.master.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.master.initialSize=5
spring.datasource.master.minIdle=5
spring.datasource.master.maxActive=20
spring.datasource.master.maxWait=60000
spring.datasource.master.timeBetweenEvictionRunsMillis=60000
spring.datasource.master.minEvictableIdleTimeMillis=300000
spring.datasource.master.validationQuery=SELECT 1 FROM DUAL
spring.datasource.master.testWhileIdle=true
spring.datasource.master.testOnBorrow=false
spring.datasource.master.testOnReturn=false
spring.datasource.master.poolPreparedStatements=true
spring.datasource.master.maxPoolPreparedStatementPerConnectionSize=20
#spring.datasource.filters=stat,wall,log4j
spring.datasource.master.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
#slave
spring.datasource.slave.url=jdbc:mysql://127.0.0.1:3306/anyikongku_escape1?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false&allowMultiQueries=true&autoReconnect=true&allowPublicKeyRetrieval=true
spring.datasource.slave.username=root
spring.datasource.slave.password=sql
spring.datasource.slave.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.slave.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.slave.initialSize=5
spring.datasource.slave.minIdle=5
spring.datasource.slave.maxActive=20
spring.datasource.slave.maxWait=60000
spring.datasource.slave.timeBetweenEvictionRunsMillis=60000
spring.datasource.slave.minEvictableIdleTimeMillis=300000
spring.datasource.slave.validationQuery=SELECT 1 FROM DUAL
spring.datasource.slave.testWhileIdle=true
spring.datasource.slave.testOnBorrow=false
spring.datasource.slave.testOnReturn=false
spring.datasource.slave.poolPreparedStatements=true
spring.datasource.slave.maxPoolPreparedStatementPerConnectionSize=20
#spring.datasource.filters=stat,wall,log4j
spring.datasource.slave.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
数据源配置
package com.boot.springbootmvc.config.druid;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Configuration
public class DataSourceConfig {
@Primary
@Bean(name = "first")
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSource first() {
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "second")
@ConfigurationProperties(prefix = "spring.datasource.slave")
public DataSource second() {
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "third")
@ConfigurationProperties(prefix = "spring.datasource.slave1")
public DataSource third() {
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "dynamicRoutingDataSource")
public DataSource dynamicRoutingDataSource(){
DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
Map<Object, Object> dataSourceMap = new HashMap<Object, Object>(3);
dataSourceMap.put(DataSourceKey.MASTER, first());
dataSourceMap.put(DataSourceKey.RADIUS, second());
dataSourceMap.put(DataSourceKey.DHCP, third());
dynamicRoutingDataSource.setTargetDataSources(dataSourceMap);
DynamicDataSourceHolder.dataSourceKeys.addAll(dataSourceMap.keySet());
return dynamicRoutingDataSource;
}
@Bean(name="sqlSessionFactory")
public SqlSessionFactory sqlSessionFactorys() throws Exception {
log.info("-------------------- sqlSessionFactory init ---------------------");
try {
SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
sessionFactoryBean.setDataSource(dynamicRoutingDataSource());
// 读取配置
sessionFactoryBean.setTypeAliasesPackage("com.boot.springbootmvc.entity");
//设置mapper.xml文件所在位置
Resource[] resources = new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml");
sessionFactoryBean.setMapperLocations(resources);
return sessionFactoryBean.getObject();
} catch (IOException e) {
log.error("mybatis resolver mapper*xml is error", e);
return null;
} catch (Exception e) {
log.error("mybatis sqlSessionFactoryBean create error", e);
return null;
}
}
}
常量配置
package com.boot.springbootmvc.config.druid;
public class DataSourceKey {
public static final String MASTER = "anyi";
public static final String RADIUS = "radius";
public static final String DHCP = "dhcp";
}
配置切面动态切换
package com.boot.springbootmvc.config.druid;
import java.lang.reflect.Method;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Aspect
@Component
@Order(0)
public class DynamicDataSourceAspect {
@Pointcut("execution(* com.boot.springbootmvc.dao.*.*(..))")
public void dataSourcePointcut(){
}
@Before("dataSourcePointcut()")
public void before(JoinPoint joinPoint) {
Object target = joinPoint.getTarget();
String method = joinPoint.getSignature().getName();
Class<?>[] clazz = target.getClass().getInterfaces();
Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameterTypes();
try {
Method m = clazz[0].getMethod(method, parameterTypes);
if (clazz[0].isAnnotationPresent(TargetDataSource.class)) {
TargetDataSource source = clazz[0].getAnnotation(TargetDataSource.class);
DynamicDataSourceHolder.removeDataSourceRouterKey();
DynamicDataSourceHolder.setDataSourceRouterKey(source.value());
} else if (m != null && m.isAnnotationPresent(TargetDataSource.class)) { //如果方法上存在切换数据源的注解,则根据注解内容进行数据源切换
TargetDataSource source = m.getAnnotation(TargetDataSource.class);
DynamicDataSourceHolder.removeDataSourceRouterKey();
DynamicDataSourceHolder.setDataSourceRouterKey(source.value());
log.info(String.format("class[%s],method[%s],使用数据源[%s]", clazz[0].getName(), m.getName(),
DynamicDataSourceHolder.getDataSourceRouterKey()));
} else {
DynamicDataSourceHolder.removeDataSourceRouterKey();
DynamicDataSourceHolder.setDataSourceRouterKey(DataSourceKey.MASTER);
log.debug("switch datasource fail,use default");
}
} catch (Exception e) {
log.error("current thread " + Thread.currentThread().getName() + " add data to ThreadLocal error", e);
}
}
@After("dataSourcePointcut()")
public void after(JoinPoint joinPoint){
DynamicDataSourceHolder.removeDataSourceRouterKey();
}
}
package com.boot.springbootmvc.config.druid;
import java.util.ArrayList;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class DynamicDataSourceHolder {
/**
* 存储已经注册的数据源的key
*/
public static final List<Object> dataSourceKeys = new ArrayList<>();
/**
* 线程级别的私有变量
*/
private static final ThreadLocal<String> HOLDER = new ThreadLocal<>();
public static String getDataSourceRouterKey () {
String db=HOLDER.get();
if (db == null) {
db = DataSourceKey.MASTER;
// 默认是读写库
}
return db;
}
public static void setDataSourceRouterKey (String dataSourceRouterKey) {
log.info("切换至{}数据源", dataSourceRouterKey);
HOLDER.set(dataSourceRouterKey);
}
/**
* 设置数据源之前一定要先移除
*/
public static void removeDataSourceRouterKey () {
HOLDER.remove();
}
/**
* 判断指定DataSrouce当前是否存在
*
* @param dataSourceId
* @return
*/
public static boolean containsDataSource(String dataSourceId){
return dataSourceKeys.contains(dataSourceId);
}
}
package com.boot.springbootmvc.config.druid;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicRoutingDataSource extends AbstractRoutingDataSource{
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceHolder.getDataSourceRouterKey();
}
}
package com.boot.springbootmvc.config.druid;
import java.lang.annotation.*;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TargetDataSource {
String value() default DataSourceKey.MASTER;
}
mapper层
@TargetDataSource(DataSourceKey.RADIUS)
public Map<String, Object> getSlave();