1. nacos中添加多数据源配置
spring:
datasource:
master:
url: jdbc:mysql://base-mysql:3306/XXX?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
name: master
initialize: true
filters: stat
slave1:
url: jdbc:mysql://base-mysql:3306/XXX?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
name: slave1
initialize: true
filters: stat
这样就配置好了两个数据源分别为 master和slave1
然后还要去springboot 启动页面添加
之后自定义的数据源的注解
2. 自定义数据源切换注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface DB{
String value() default "master";
}
3. 数据源切换操作配置
当前线程上下文数据源标识类
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DynamicDataSourceSwitcher {
static Logger logger = LoggerFactory.getLogger(DynamicDataSourceSwitcher.class);
public static final String Mater = "master";
public static final String Slave1 = "slave1";
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public static void setDataSource(String name){
logger.info("-------- 设置数据源数据源为 :{} ", name);
contextHolder.set(name);
}
public static String getDataSource(){
if (StringUtils.isEmpty(contextHolder.get())) {
setDataSource(Mater);
}
return contextHolder.get();
}
public static void cleanDataSource(){
contextHolder.remove();
}
}
动态数据源切换 , 核心就是通过这个类实现多数据源的额动态切换
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
Logger logger = LoggerFactory.getLogger(DynamicDataSource.class);
@Override
protected Object determineCurrentLookupKey() {
logger.info("------------------当前数据源 {}", DynamicDataSourceSwitcher.getDataSource());
return DynamicDataSourceSwitcher.getDataSource();
}
}
多数据源bean的配置类
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
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 javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class MultipleDataSourceConfig {
@Bean("master")
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSource createMasterDataSource(){
return new DruidDataSource();
}
@Bean("slave1")
@ConfigurationProperties(prefix = "spring.datasource.slave1")
public DataSource createSlave1DataSource(){
return new DruidDataSource();
}
/**
* 设置动态数据源,通过@Primary 来确定主DataSource
* @return
*/
@Bean
@Primary
public DataSource createDynamicDataSource(@Qualifier("master") DataSource master, @Qualifier("slave1") DataSource slave1){
DynamicDataSource dynamicDataSource = new DynamicDataSource();
//设置默认数据源
dynamicDataSource.setDefaultTargetDataSource(master);
//配置多数据源
Map<Object, Object> map = new HashMap<>();
map.put("master",master);
map.put("slave1",slave1);
dynamicDataSource.setTargetDataSources(map);
return dynamicDataSource;
}
}
aop切面实现多数据源切换
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* 切换数据源
* 选择非master数据源时, 通过切面切换数据源
*/
@Aspect
@Component
@Order(1)
public class DynamicDataSourceAspect {
private Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class);
/**
* 切入点只对@Service注解的类上的@Db注解生效 , 无注解 , 默认使用master
* @param db
*/
@Pointcut(value="@within(org.springframework.stereotype.Service) && @annotation(db)" )
public void dynamicDataSourcePointCut(DB db){}
@Before(value = "dynamicDataSourcePointCut(db)")
public void switchDataSource(DB db) {
DynamicDataSourceSwitcher.setDataSource(db.value());
}
/**
* 切点执行完后 切换成主数据库
* @param db
*/
@After(value="dynamicDataSourcePointCut(db)")
public void after(DB db){
DynamicDataSourceSwitcher.cleanDataSource();
}
}
4. 使用
@Override
@DB(value = DynamicDataSourceSwitcher.Slave1)
public List<Map> getUser() {
return userMapper.getUser();
}
@Override
@DB(value = DynamicDataSourceSwitcher.Master)
public List<Map> getInstrumentList() {
return instrumentMapper.getInstrumentList();
}
最后 :
@SpringBootApplication(scanBasePackages = {"org.orient.otc"},exclude = {DataSourceAutoConfiguration.class})
exclude = {DataSourceAutoConfiguration.class}这个东西你不加上(自定义数据源一定要排除SpringBoot自动配置数据源) , 就会在启动项目时报错循环依赖;
org.springframework.boot.actuate.autoconfigure.jdbc.DataSourceHealthIndicatorAutoConfiguration
┌─────┐
| dataSource
↑ ↓
| scopedTarget.dataSource defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]
↑ ↓
| org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker
└─────┘