springboot有时候会使用到多数据源,比如mysql、oracle同时使用,或者其他数据库;
大体思路是这么样呢?
思路:在yml里面配置两个数据源(这里以两个为例,也可以配多个数据源),在DataSourceContextHolder配置,一个默认数据源,还有就是其他数据源,当使用注解时(自定义注解),把指定的要使用的数据源注入使用,不使用注解就取默认的注解。
第一步:写个枚举方便取数据源
/**
* 枚举,区分数据源
*/
public enum DataSourceEnum {
DB1("db1"),
DB2("db2");
private String value;
DataSourceEnum(String value){
this.value=value;
}
public String getValue(){
return value;
}
}
第二步:创建一个线程安全的DatabaseType,存放数据源
/**
* 保存一个线程安全的DatabaseType容器
* @author Admin
* @Date 2020/04/19
*/
public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new InheritableThreadLocal<>();
public static String getDataSource() {
return contextHolder.get();
}
public static void setDataSource(DataSourceEnum dataSource){
contextHolder.set(dataSource.getValue());
}
public static void clear(){
contextHolder.remove();
}
}
第三步:写一个获取数据源的类
/**
* 动态数据源,需要继承AbstractRoutingDataSource
* 作用:使用DatabaseContextHolder获取当前线程的DatabaseType
* @author Admin
*/
public class MultipleDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSource();
}
}
第四步(最核心):初始化数据源,并设置默认数据源
/**
* springboot集成mybatis基本入口
* 1.创建数据源
* 2.创建sqlsessionFactory
* @author Admin
*/
@Configuration
public class MybatisPlusConfiguration {
/**
* @ConfigurationProperties 的大致作用就是:赋值,将注解转换成对象。给对象赋值
* @return
*/
@Bean(name="db1")
@ConfigurationProperties(prefix = "spring.datasource.druid.db1")
public DataSource db1(){
return DruidDataSourceBuilder.create().build();
}
@Bean(name="db2")
@ConfigurationProperties(prefix = "spring.datasource.druid.db2")
public DataSource db2(){
return DruidDataSourceBuilder.create().build();
}
/**
* 创建动态数据源
* @primary: 表示在同一个接口有多个类可以注入的时候,默认选择哪个,而不是让@Autowire报错,spring扫码注入bean时优先注入带有@Primary注解的bean
*@Qualifier:
* 当有多个同一类型的Bean时,可以用@Qualifier(“name”)来指定。@Qualifier限定描述符除了能根据名字进行注入,还能进行更细粒度的控制如何选择候选者
*/
@Bean
@Primary
public DataSource multipleDataSource(@Qualifier("db1") DataSource db1,
@Qualifier("db2") DataSource db2){
MultipleDataSource dataSource = new MultipleDataSource();
Map<Object,Object> targetDataSource = new HashMap<>();
targetDataSource.put(DataSourceEnum.DB1.getValue(),db1);
targetDataSource.put(DataSourceEnum.DB2.getValue(),db2);
System.out.println("我是目标数据源:"+targetDataSource);
dataSource.setTargetDataSources(targetDataSource);
/**
* setDefaultTargetDataSource:当不加注解时,就取默认的数据源
* 在这里设置默认数据源
*/
dataSource.setDefaultTargetDataSource(db1);
System.out.println("我是最终数据源:"+dataSource.determineCurrentLookupKey());
return dataSource;
}
}
至此,可以取默认数据源了,也就是不写注解的(前提是配置了yml)
第五步:写自定义注解
/**
* 数据源注解
* @author Admin
*/
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
DataSourceEnum value() default DataSourceEnum.DB1;
}
第六步:注解的切面
/**
* @author Admin
* @Order(-1)保证该AOP在@Transactional之前执行,如果没有order,而且类配置了@Transaction那么数据源会失效
* 通过order指定顺序,值越小越先执行,不标注数字则默认int最大值2147483647,即优先级最低
*/
@Component
@Aspect
@Order(-1)
@Slf4j
public class DataSourceAspect {
@Pointcut("@annotation(com.sinux.liaochao.multidatasource.multipartidatasource.config.DataSource)")
public void pointCut(){
}
@Before("pointCut()")
public void doBefore(JoinPoint joinPoint){
System.out.println("我是注解切面:=================================================");
Object target = joinPoint.getTarget();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
DataSourceEnum dataSource = null;
try {
Method method = target.getClass().getMethod(signature.getName(),signature.getParameterTypes());
if(method.isAnnotationPresent(DataSource.class)){
DataSource annotation = method.getAnnotation(DataSource.class);
dataSource = annotation.value();
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
log.info("选择数据源========",dataSource.getValue());
DataSourceContextHolder.setDataSource(dataSource);
}
@After("pointCut()")
public void doAfter(){
DataSourceContextHolder.clear();
}
}
自此主业完成。
第七步:配置yml
spring:
application:
name: multiDatasource
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
db1:
url: jdbc:mysql://192.168.61.147:3306/tensquare_article?useSSL=false&allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
username: root
password: 123456
db2:
url: jdbc:mysql://192.168.61.148:3306/tensquare_article?useSSL=false&allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
username: root
password: 123456
代码中使用:(只举一个例子 @DataSource(DataSourceEnum.DB2)表示使用db2数据源
不配注解或者配DataSourceEnum.DB1都是使用数据源1,因为dataSource.setDefaultTargetDataSource(db1);设置默认为db1)
/**
* 查询数据源2的内容
* @return
*/
@Override
@DataSource(DataSourceEnum.DB2)
public CommonResult<JSONObject> queryDB2() {
List<JSONObject> list = multipartiDataSourceMapper.queryDB2();
JSONObject js = new JSONObject();
js.put("list",list);
js.put("code",200);
js.put("msg","查询成功!");
js.put("message","success");
return CommonResult.success(js);
}
<dependency>
<groupId>com.baomidougroupId>
<artifactId>dynamic-datasource-spring-boot-starterartifactId>
<version>1.3.3version>
dependency>
spring:
datasource:
dynamic:
primary: master #设置默认的数据源或者数据源组,默认值即为master
strict: false #设置严格模式,默认false不启动. 启动后在未匹配到指定数据源时候会抛出异常,不启动则使用默认数据源.
datasource:
master:
url: jdbc:mysql://xx.xx.xx.xx:3306/dynamic
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置
slave_1:
url: jdbc:mysql://xx.xx.xx.xx:3307/dynamic
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
slave_2:
url: ENC(xxxxx) # 内置加密,使用请查看详细文档
username: ENC(xxxxx)
password: ENC(xxxxx)
driver-class-name: com.mysql.jdbc.Driver
schema: db/schema.sql # 配置则生效,自动初始化表结构
data: db/data.sql # 配置则生效,自动初始化数据
continue-on-error: true # 默认true,初始化失败是否继续
separator: ";" # sql默认分号分隔符
#......省略
#以上会配置一个默认库master,一个组slave下有两个子库slave_1,slave_2
使用 @DS 切换数据源。
@DS 可以注解在方法上和类上,同时存在方法注解优先于类上注解。
强烈建议只注解在service实现上
@Service
@DS(“slave”)
public class UserServiceImpl implements UserService {
}