三个测试数据库SQL:
data01
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
DROP TABLE IF EXISTS `tb_user`; CREATE TABLE `tb_user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `age` int(11) NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `tb_user` VALUES (1, '001', 10); INSERT INTO `tb_user` VALUES (2, '002', 20);
|
data02
1 2 3 4 5 6 7 8 9 10 11 12 13
| DROP TABLE IF EXISTS `tb_user`; CREATE TABLE `tb_user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `age` int(11) NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `tb_user` VALUES (1, '003', 30); INSERT INTO `tb_user` VALUES (2, '004', 40);
|
data03
1 2 3 4 5 6 7 8 9 10 11 12 13
| DROP TABLE IF EXISTS `tb_user`; CREATE TABLE `tb_user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `age` int(11) NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `tb_user` VALUES (1, '005', 50); INSERT INTO `tb_user` VALUES (2, '006', 60);
|
Pom.xml
- 这是刚新建SpringBoot项目的pom.xml依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
|
1 2 3 4 5 6 7 8
| <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency>
|
1 2 3 4 5 6 7 8
| <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> </dependency>
|
application.yml配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| spring: datasource: local: username: root password: 123456 jdbc-url: jdbc:mysql://127.0.0.1:3306/data01?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai driver-class-name: com.mysql.cj.jdbc.Driver enabled: true remote: username: root password: 123456 jdbc-url: jdbc:mysql://127.0.0.1:3306/data02?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai driver-class-name: com.mysql.cj.jdbc.Driver three: username: root password: 123456 jdbc-url: jdbc:mysql://127.0.0.1:3306/data03?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai driver-class-name: com.mysql.cj.jdbc.Driver enabled: true
|
注意:各个版本的 springboot
配置 datasource
时参数有所变化,例如低版本配置数据库 url
时使用 url
属性,高版本使用 jdbc-url
属性
连接数据源配置文件
1 2 3 4 5
| public enum DataSourceType { REMOTE, LOCAL, THREE }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public class DynamicDataSourceContextHolder {
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
public static void setDataSource(String dataSourceType){ System.out.printf("切换到{%s}数据源",dataSourceType); CONTEXT_HOLDER.set(dataSourceType); }
public static String getDataSourceType(){ return CONTEXT_HOLDER.get(); }
public static void clearDataSourceType(){ CONTEXT_HOLDER.remove(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class DynamicDataSource extends AbstractRoutingDataSource {
public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object,Object> targetDataSource){ super.setDefaultTargetDataSource(defaultTargetDataSource); super.setTargetDataSources(targetDataSource); super.afterPropertiesSet(); }
@Override protected Object determineCurrentLookupKey() { return DynamicDataSourceContextHolder.getDataSourceType(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| @Configuration public class DataSourceConfig {
@Bean @ConfigurationProperties("spring.datasource.remote") public DataSource remoteDataSource(){ return DataSourceBuilder.create().build(); }
@Bean @ConfigurationProperties("spring.datasource.local") @ConditionalOnProperty(prefix = "spring.datasource.local",name = "enabled",havingValue = "true") public DataSource localDataSource(){ return DataSourceBuilder.create().build(); }
@Bean @ConfigurationProperties("spring.datasource.three") @ConditionalOnProperty(prefix = "spring.datasource.three",name = "enabled",havingValue = "true") public DataSource threeDataSource(){ return DataSourceBuilder.create().build(); }
@Bean @Primary public DynamicDataSource dynamicDataSource(DataSource remoteDataSource,DataSource localDataSource,DataSource threeDataSource){ Map<Object,Object> targetDataSources = new HashMap<>(); targetDataSources.put(DataSourceType.REMOTE.name(),remoteDataSource); targetDataSources.put(DataSourceType.LOCAL.name(),localDataSource); targetDataSources.put(DataSourceType.THREE.name(),threeDataSource); return new DynamicDataSource(remoteDataSource,targetDataSources); } }
|
自定义注解:
1 2 3 4 5 6 7
| @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface DataSource {
DataSourceType value() default DataSourceType.REMOTE; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @Aspect @Order(1) @Component public class DataSourceAspect {
@Pointcut("@annotation(com.example.demo.mult.DataSource)") public void dsPointCut(){}
@Around("dsPointCut()") public Object around(ProceedingJoinPoint point) throws Throwable{ MethodSignature signature = (MethodSignature) point.getSignature(); Method method = signature.getMethod(); DataSource dataSource = method.getAnnotation(DataSource.class); if (null!=dataSource){ DynamicDataSourceContextHolder.setDataSource(dataSource.value().name()); } try { return point.proceed(); }finally { DynamicDataSourceContextHolder.clearDataSourceType(); } } }
|
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @RestController public class TestController {
@Resource private JdbcTemplate jdbcTemplate;
@GetMapping("/data1") @DataSource(value = DataSourceType.LOCAL) public List<Map<String, Object>> getData1(){ return jdbcTemplate.queryForList("select * from tb_user"); }
@GetMapping("/data2") @DataSource(value = DataSourceType.REMOTE) public List<Map<String, Object>> getData2(){ return jdbcTemplate.queryForList("select * from tb_user"); }
@GetMapping("/data3") @DataSource(value = DataSourceType.THREE) public List<Map<String, Object>> getData3(){ return jdbcTemplate.queryForList("select * from tb_user"); } }
|