Spring boot 整合dynamic实现多数据源

Scroll Down

项目git地址:Jacob-dynamic

准备工作

# 创建数据库db1
CREATE DATABASE `db1`CHARACTER SET utf8 COLLATE utf8_bin
# 创建user表
CREATE TABLE `user` (
  `id` int(11) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
# 添加数据
INSERT INTO `db1`.`user` (`id`, `name`) VALUES (1, 'db1_1'); 
INSERT INTO `db1`.`user` (`id`, `name`) VALUES (2, 'db1_2'); 
INSERT INTO `db1`.`user` (`id`, `name`) VALUES (3, 'db1_3'); 
INSERT INTO `db1`.`user` (`id`, `name`) VALUES (4, 'db1_4'); 
INSERT INTO `db1`.`user` (`id`, `name`) VALUES (5, 'db1_5'); 

# 创建数据库db2
CREATE DATABASE `db2`CHARACTER SET utf8 COLLATE utf8_bin
# 创建user表
CREATE TABLE `user` (
  `id` int(11) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
# 添加数据
INSERT INTO `db2`.`user` (`id`, `name`) VALUES (1, 'db2_1'); 
INSERT INTO `db2`.`user` (`id`, `name`) VALUES (2, 'db2_2'); 
INSERT INTO `db2`.`user` (`id`, `name`) VALUES (3, 'db2_3'); 
INSERT INTO `db2`.`user` (`id`, `name`) VALUES (4, 'db2_4'); 
INSERT INTO `db2`.`user` (`id`, `name`) VALUES (5, 'db2_5'); 

创建项目

最终目录结果

image-20200814155826016

添加依赖

        <!-- mysql start -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- mysql end -->
        <!-- web start -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- web end -->
        <!-- lombok start  -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- lombok end -->
        <!-- hutool start -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>${hutool.versiont}</version>
        </dependency>
        <!-- hutool end -->
        <!-- configuration start -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- configuration end -->
        <!-- swagger start -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>swagger-bootstrap-ui</artifactId>
            <version>1.9.6</version>
        </dependency>
        <!-- swagger end -->
        <!-- mybatis-plus start -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.1.0</version>
        </dependency>
        <!-- mybatis-plus end -->
        <!-- spring aop start -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <!-- spring aop end -->

添加配置

server:
  port: 8085

spring:
  datasource:
    db1:
      type: com.zaxxer.hikari.HikariDataSource
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: 123456
      hikari:
        maximum-pool-size: 20
        minimum-idle: 2
      url: jdbc:mysql://localhost:3306/springbootdb?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai
    db2:
      type: com.zaxxer.hikari.HikariDataSource
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: 123456
      hikari:
        maximum-pool-size: 20
        minimum-idle: 2
      url: jdbc:mysql://localhost:3306/springbootdb2?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai

定义枚举

package cn.org.jacob.enums;
//...
public enum DataSourceTypeEnum {
    DB1,
    DB2
}

编写aop

package cn.org.jacob.aop;
//...
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface DataSource {

    DataSourceTypeEnum value() default DataSourceTypeEnum.DB1;

}
package cn.org.jacob.aop;
//...
@Slf4j
@Aspect
@Component
@Order(-1)
public class DataSourceAspect {

    @Pointcut("@annotation(cn.org.jacob.aop.DataSource)")
    public void dataSourceAspectMethod() {

    }

    @Before(value = "dataSourceAspectMethod()")
    public void beforeDataSource(JoinPoint joinPoint) {
//        log.info("进入切点>>>>>>>>>>>>>>>>>>>>>>>>>>>执行切换数据源");
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        DataSource annotation = signature.getMethod().getAnnotation(DataSource.class);
        DataSourceTypeEnum value = annotation.value();
        DataSourceContextHolder.setDataSource(value);
        log.info("当前使用的数据源:{}", value);
    }

    @After("dataSourceAspectMethod()")
    public void afterDataSource() {
        DataSourceContextHolder.clear();
    }
}
package cn.org.jacob.aop;
//...
public class DataSourceContextHolder {

    /**
     * 吧数据源存放当前线程
     */
    private static final ThreadLocal<DataSourceTypeEnum> contextHolder = new ThreadLocal<>();

    /**
     * 设置数据源
     *
     * @param dataSource 当前的数据源
     */
    public static void setDataSource(DataSourceTypeEnum dataSource) {
        contextHolder.set(dataSource);
    }

    /**
     * 获取数据源
     *
     * @return 当前数据源
     */
    public static DataSourceTypeEnum getDataSource() {
        return contextHolder.get();
    }

    /**
     * 删除当前线程上下文的数据源信息
     */
    public static void clear() {

        contextHolder.remove();
    }

}

编写配置

package cn.org.jacob.config;
//..
@Configuration
@EnableSwagger2
public class SwaggerConfiguration {

    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("cn.org.jacob"))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("swagger-bootstrap-ui RESTful APIs")
                .description("swagger-bootstrap-ui")
                .termsOfServiceUrl("http://localhost:8081/")
                .contact("m15870979735@163.com")
                .version("1.0")
                .build();
    }
}
package cn.org.jacob.config;
//..
@Data
@Component
@ConfigurationProperties(prefix = "spring.datasource.db1")
public class Db1Config {

    private String driverClassName;

    private String userName;

    private String password;

    private String url;

}
package cn.org.jacob.config;
//..
@Data
@Component
@ConfigurationProperties(prefix = "spring.datasource.db2")
public class Db2Config {

    private String driverClassName;

    private String userName;

    private String password;

    private String url;
}

@Slf4j
public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSource();
    }
}

package cn.org.jacob.config;
//..
@Configuration
@MapperScan("cn.org.jacob.mapper")
@Slf4j
public class MybatisPlusConfigurer {

	@Autowired
	Db1Config db1Config;

	@Autowired
	Db2Config db2Config;


	/**
	 * 分页插件
	 *
	 * @return PaginationInterceptor
	 */
	@Bean
	public PaginationInterceptor paginationInterceptor() {
		return new PaginationInterceptor();
	}

	@Primary
	@Bean(value = "db1DataSource")
	public DataSource roadDataSource() {

		return DataSourceBuilder.create().url(db1Config.getUrl())
				.driverClassName(db1Config.getDriverClassName()).username(db1Config.getUserName())
				.password(db1Config.getPassword()).type(HikariDataSource.class).build();
	}

	@Bean(value = "db2DataSource")
	public DataSource garageDataSource() {

		return DataSourceBuilder.create().url(db2Config.getUrl())
				.driverClassName(db2Config.getDriverClassName()).username(db2Config.getUserName())
				.password(db2Config.getPassword()).type(HikariDataSource.class).build();
	}

	@Bean
	public DynamicDataSource dataSource(@Qualifier("db1DataSource") DataSource db1DataSource,
			@Qualifier("db2DataSource") DataSource db2DataSource) {

		Map<Object, Object> map = new HashMap<>(2);
		map.put(DataSourceTypeEnum.DB1, db1DataSource);
		map.put(DataSourceTypeEnum.DB2, db2DataSource);
		DynamicDataSource dynamicDataSource = new DynamicDataSource();
		dynamicDataSource.setTargetDataSources(map);
		dynamicDataSource.setDefaultTargetDataSource(db1DataSource);
		return dynamicDataSource;
	}

	@Bean
	public MybatisSqlSessionFactoryBean sqlSessionFactoryBean() throws Exception {
		MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
		// 重新设置数据源 DB1,DB2 两个
		sqlSessionFactoryBean.setDataSource(dataSource(roadDataSource(), garageDataSource()));
		Resource[] resources = new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml");
		sqlSessionFactoryBean.setMapperLocations(resources);
		// 手动设置分页插件
		sqlSessionFactoryBean.setPlugins(new Interceptor[] { paginationInterceptor() });

		return sqlSessionFactoryBean;
	}

	@Bean
	public PlatformTransactionManager transactionManager(DynamicDataSource dynamicDataSource) {
		return new DataSourceTransactionManager(dynamicDataSource);
	}
}

实体类,Mapper,service忽略

编写控制器

@RestController
@Slf4j
@Api(value = "用户", tags = {"用户"})
@AllArgsConstructor
@RequestMapping("user")
public class UserController {

    private UserService userService;

    @GetMapping("db1List")
    @ApiOperation(value = "获取db1List")
    @DataSource(DataSourceTypeEnum.DB1)
    public List<User> db1List(){
        return userService.list();
    }

    @GetMapping("db2List")
    @ApiOperation(value = "获取db2List")
    @DataSource(DataSourceTypeEnum.DB2)
    public List<User> db2List(){
        return userService.list();
    }

}

访问查看效果

image-20200814160526575

image-20200814160608915