一、前言
多数据源的应用场景大多为数据迁移和读写分离,这里的业务场景是数据的采集与再入库
比较重要的一点就是AbstractRoutingDataSource,下边是简单源码说明
二、示例
pom文件
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.0.5.RELEASE
com.example
multi-data-source
0.0.1-SNAPSHOT
multi-data-source
Demo project for Spring Boot
1.8
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-jdbc
org.springframework.boot
spring-boot-starter-web
org.mybatis.spring.boot
mybatis-spring-boot-starter
1.3.2
mysql
mysql-connector-java
runtime
org.springframework.boot
spring-boot-starter-tomcat
provided
org.springframework.boot
spring-boot-starter-data-redis
com.aliyun
aliyun-java-sdk-core
3.2.4
com.github.penggle
kaptcha
2.3.2
com.alibaba
druid-spring-boot-starter
1.1.9
com.alibaba
fastjson
1.2.47
org.projectlombok
lombok
1.18.2
provided
org.springframework.boot
spring-boot-starter-aop
org.apache.httpcomponents
httpclient
4.5.6
commons-fileupload
commons-fileupload
1.4
com.github.pagehelper
pagehelper-spring-boot-starter
1.2.10
com.squareup.okhttp3
okhttp
3.10.0
io.springfox
springfox-swagger2
2.9.2
io.springfox
springfox-swagger-ui
2.9.2
commons-codec
commons-codec
1.11
joda-time
joda-time
2.9.9
org.springframework.boot
spring-boot-starter-websocket
org.bouncycastle
bcprov-jdk15on
1.57
com.google.zxing
core
3.3.1
com.google.zxing
javase
3.3.1
org.snmp4j
snmp4j
2.7.0
maven-compiler-plugin
1.8
org.springframework.boot
spring-boot-maven-plugin
org.mybatis.generator
mybatis-generator-maven-plugin
1.3.5
true
true
application.yml
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.example.multidatasource.model
pagehelper:
helperDialect: mysql
reasonable: true
supportMethodsArguments: true
params: count=countSql
logging:
level:
com.example.multidatasource.dao: DEBUG
file: log/log.log
spring:
profiles:
active: test
application-test.yml
server:
port: 8083
tomcat:
uri-encoding: UTF-8
servlet:
context-path: /
spring:
application:
name: data-default
datasource:
kanyun: #数据源1
name: kanyun
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:3307/kanyun??useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false
username: root
password: admin
max-pool-size: 20
max-active: 10
max-idle: 5
min-idle: 2
initial-size: 2
validation-query: select 1
test-on-borrow: true
test-on-return: false
test-while-idle: false
time-between-eviction-runs-millis: 3000
min-evictable-idle-time-millis: 3000
max-wait: 3000
jmx-enabled: true
sgdata: #数据源2
name: sgdata
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:3307/sgdatabase??useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false
username: root
password: admin
max-pool-size: 20
max-active: 10
max-idle: 5
min-idle: 2
initial-size: 2
validation-query: select 1
test-on-borrow: true
test-on-return: false
test-while-idle: false
time-between-eviction-runs-millis: 3000
min-evictable-idle-time-millis: 3000
max-wait: 3000
jmx-enabled: true
mvc:
static-path-pattern: /
view:
prefix: /views/
suffix: .html
resources:
static-locations: classpath:/static/
http:
encoding:
charset: utf-8
force: true
enabled: true
DataSourceContextHolder
package com.example.multidatasource.bean;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class DataSourceContextHolder {
/** 本地线程共享对象(保证在同一线程下切换后不要被其他线程修改) */
private final static ThreadLocal local = new ThreadLocal<>();
public static void putDataSource(String name){
local.set(name);
}
public static String getDataSource(){
return local.get();
}
public static void removeDataSource(){
local.remove();
}
}
MultipleDataSource
package com.example.multidatasource.bean;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class MultipleDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
//从共享线程中获取数据源名称
return DataSourceContextHolder.getDataSource();
}
}
DataSource 注解
package com.example.multidatasource.bean;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
String value() default "";
String kanyun = "kanyun";
String sgdata = "sgdata";
}
注解切面
package com.example.multidatasource.bean;
import lombok.extern.slf4j.Slf4j;
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.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component
@Slf4j
@Aspect
@Order(-1)
public class DataSourceAspect {
@Pointcut("@annotation(com.example.multidatasource.bean.DataSource)")
public void pointCut(){
}
@Before("pointCut() && @annotation(dataSource)")
public void doBefore(DataSource dataSource){
log.info("选择数据源---"+dataSource.value());
DataSourceContextHolder.putDataSource(dataSource.value());
}
@After("pointCut()")
public void doAfter(){
DataSourceContextHolder.removeDataSource();
}
}
配置动态数据源
package com.example.multidatasource.config;
import com.example.multidatasource.bean.MultipleDataSource;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
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
@MapperScan("com.example.multidatasource.dao")
public class DataSourceConfig {
@Bean(name = "kanyunDataSource")
@Qualifier("kanyunDataSource")
@ConfigurationProperties(prefix = "spring.datasource.kanyun")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "sgdataDataSource")
@Qualifier("sgdataDataSource")
@ConfigurationProperties(prefix = "spring.datasource.sgdata")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
/**
* 动态数据源配置
* @return
*/
@Bean
@Primary
public DataSource multipleDataSource(@Qualifier("kanyunDataSource") DataSource db1,
@Qualifier("sgdataDataSource") DataSource db2) {
MultipleDataSource multipleDataSource = new MultipleDataSource();
Map< Object, Object > targetDataSources = new HashMap<>();
targetDataSources.put(com.example.multidatasource.bean.DataSource.kanyun, db1);
targetDataSources.put(com.example.multidatasource.bean.DataSource.sgdata, db2);
//添加数据源
multipleDataSource.setTargetDataSources(targetDataSources);
//设置默认数据源
multipleDataSource.setDefaultTargetDataSource(db1);
return multipleDataSource;
}
}
定时测试
package com.example.multidatasource.service;
import com.example.multidatasource.dao.ActivesignalMapper;
import com.example.multidatasource.dao.App_accesslogMapper;
import com.example.multidatasource.model.Activesignal;
import com.example.multidatasource.model.App_accesslog;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
@Slf4j
public class TestService {
@Autowired
ActivesignalMapper activesignalMapper;
@Autowired
App_accesslogMapper app_accesslogMapper;
@Scheduled(initialDelay = 1000*5,fixedRate = 1000*60*60*2400)
public void exec(){
List list1 = app_accesslogMapper.queryForList();
list1.forEach(activesignal -> log.info("获取数据1:{}",activesignal));
List list2 = activesignalMapper.queryForList();
list2.forEach(activesignal -> log.info("获取数据2:{}",activesignal));
}
}
下边是启动类
package com.example.multidatasource;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
@ComponentScan(basePackages = {"com.example"})
@ServletComponentScan(basePackages = {"com.example"})
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableScheduling
public class MultiDataSourceApplication {
public static void main(String[] args) {
SpringApplication.run(MultiDataSourceApplication.class, args);
}
@Bean
public TaskScheduler taskScheduler(){
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(10);
threadPoolTaskScheduler.setThreadNamePrefix("task");
return threadPoolTaskScheduler;
}
}
测试
结语
这里没有使用事务,如果采用事务需要分别配置每个数据源的事务,并采用事务性注解进行统一管理
-end-