From 10cb533d54163c5b112b0442a51ba9c9529b45c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E9=94=85=E7=9B=96?= <332309254@qq.com> Date: Sun, 18 Nov 2018 23:00:21 +0800 Subject: [PATCH] feat: release 2.4.0 --- .gitee/ISSUE_TEMPLATE.zh-CN.md | 29 ++ .gitee/PULL_REQUEST_TEMPLATE.zh-CN.md | 16 + .github/ISSUE_TEMPLATE | 29 ++ .github/PULL_REQUEST_TEMPLATE | 16 + .gitignore | 25 + .travis.yml | 12 + CHANGELOG.md | 122 +++++ LICENSE | 191 +++++++ README.md | 482 ++++++++++++++++++ license.txt | 14 + pom.xml | 210 ++++++++ samples/.gitignore | 25 + samples/README.md | 13 + samples/dynamic-druid-mybatis-sample/pom.xml | 31 ++ .../samples/druid/mybatis/Application.java | 16 + .../samples/druid/mybatis/entity/User.java | 34 ++ .../druid/mybatis/mapper/UserMapper.java | 17 + .../druid/mybatis/service/UserService.java | 15 + .../mybatis/service/impl/UserServiceImpl.java | 35 ++ .../src/main/resources/application.yml | 69 +++ .../src/main/resources/spy.properties | 1 + .../druid/mybatis/test/ApplicationTest.java | 65 +++ samples/dynamic-jdbc-template-sample/pom.xml | 20 + .../baomidou/samples/jdbc/Application.java | 22 + .../baomidou/samples/jdbc/entity/User.java | 34 ++ .../samples/jdbc/service/UserService.java | 14 + .../jdbc/service/impl/UserServiceImpl.java | 35 ++ .../src/main/resources/application.yml | 27 + .../samples/jdbc/test/ApplicationTest.java | 64 +++ samples/dynamic-mybatis-sample/pom.xml | 22 + .../baomidou/samples/mybatis/Application.java | 15 + .../baomidou/samples/mybatis/entity/User.java | 34 ++ .../samples/mybatis/mapper/UserMapper.java | 18 + .../samples/mybatis/service/UserService.java | 15 + .../mybatis/service/impl/UserServiceImpl.java | 35 ++ .../src/main/resources/application.yml | 27 + .../samples/mybatis/test/ApplicationTest.java | 65 +++ .../transaction-logs/tmlog49.log | 0 samples/dynamic-mybatisplus2-sample/pom.xml | 22 + .../samples/mybatisplus2/Application.java | 15 + .../samples/mybatisplus2/entity/User.java | 38 ++ .../mybatisplus2/mapper/UserMapper.java | 7 + .../mybatisplus2/service/UserService.java | 11 + .../service/impl/UserServiceImpl.java | 20 + .../src/main/resources/application.yml | 27 + .../mybatisplus2/test/ApplicationTest.java | 58 +++ samples/dynamic-mybatisplus3-sample/pom.xml | 22 + .../samples/mybatisplus/Application.java | 15 + .../samples/mybatisplus/entity/User.java | 38 ++ .../mybatisplus/mapper/UserMapper.java | 7 + .../mybatisplus/service/UserService.java | 11 + .../service/impl/UserServiceImpl.java | 20 + .../src/main/resources/application.yml | 27 + .../mybatisplus3/test/ApplicationTest.java | 57 +++ samples/dynamic-nest-sample/pom.xml | 21 + .../baomidou/samples/nest/Application.java | 15 + .../samples/nest/CreateDatabaseOnStarted.java | 39 ++ .../nest/controller/TestController.java | 34 ++ .../baomidou/samples/nest/entiry/Student.java | 34 ++ .../baomidou/samples/nest/entiry/Teacher.java | 34 ++ .../samples/nest/mapper/StudentMapper.java | 17 + .../samples/nest/mapper/TeacherMapper.java | 17 + .../samples/nest/service/SchoolService.java | 12 + .../samples/nest/service/StudentService.java | 15 + .../samples/nest/service/TeacherService.java | 18 + .../nest/service/impl/SchoolServiceImpl.java | 42 ++ .../nest/service/impl/StudentServiceImpl.java | 35 ++ .../nest/service/impl/TeacherServiceImpl.java | 45 ++ .../src/main/resources/application.yml | 27 + .../samples/nest/test/ApplicationTest.java | 81 +++ samples/dynamic-spel-sample/pom.xml | 22 + .../baomidou/samples/spel/Application.java | 15 + .../spel/controller/UserController.java | 29 ++ .../baomidou/samples/spel/entity/User.java | 44 ++ .../samples/spel/mapper/UserMapper.java | 18 + .../samples/spel/service/UserService.java | 17 + .../spel/service/impl/UserServiceImpl.java | 43 ++ .../src/main/resources/application.yml | 32 ++ .../samples/spel/test/ApplicationTest.java | 92 ++++ samples/pom.xml | 71 +++ .../datasource/AbstractRoutingDataSource.java | 63 +++ .../DynamicDataSourceClassResolver.java | 66 +++ .../datasource/DynamicDataSourceCreator.java | 238 +++++++++ .../datasource/DynamicGroupDataSource.java | 70 +++ .../datasource/DynamicRoutingDataSource.java | 167 ++++++ .../dynamic/datasource/annotation/DS.java | 39 ++ .../DynamicDataSourceAnnotationAdvisor.java | 69 +++ ...ynamicDataSourceAnnotationInterceptor.java | 74 +++ .../AbstractJdbcDataSourceProvider.java | 109 ++++ .../provider/DynamicDataSourceProvider.java | 39 ++ .../YmlDynamicDataSourceProvider.java | 63 +++ .../DefaultDynamicDataSourceSpelParser.java | 80 +++ .../DefaultDynamicDataSourceSpelResolver.java | 32 ++ .../spel/DynamicDataSourceSpelParser.java | 37 ++ .../spel/DynamicDataSourceSpelResolver.java | 34 ++ .../autoconfigure/DataSourceProperty.java | 74 +++ .../DynamicDataSourceAutoConfiguration.java | 120 +++++ .../DynamicDataSourceProperties.java | 75 +++ .../boot/autoconfigure/druid/DruidConfig.java | 244 +++++++++ .../DruidDynamicDataSourceConfiguration.java | 46 ++ .../strategy/DynamicDataSourceStrategy.java | 40 ++ .../LoadBalanceDynamicDataSourceStrategy.java | 40 ++ .../RandomDynamicDataSourceStrategy.java | 35 ++ .../DynamicDataSourceContextHolder.java | 82 +++ src/main/resources/META-INF/spring.factories | 2 + .../spring-configuration-metadata.json | 410 +++++++++++++++ target/classes/META-INF/spring.factories | 2 + .../AbstractRoutingDataSource.class | Bin 0 -> 1539 bytes .../DynamicDataSourceClassResolver.class | Bin 0 -> 2518 bytes .../datasource/DynamicDataSourceCreator.class | Bin 0 -> 7568 bytes .../datasource/DynamicGroupDataSource.class | Bin 0 -> 3872 bytes .../datasource/DynamicRoutingDataSource.class | Bin 0 -> 6398 bytes .../dynamic/datasource/annotation/DS.class | Bin 0 -> 483 bytes .../DynamicDataSourceAnnotationAdvisor.class | Bin 0 -> 2327 bytes ...namicDataSourceAnnotationInterceptor.class | Bin 0 -> 3519 bytes .../AbstractJdbcDataSourceProvider.class | Bin 0 -> 4513 bytes .../provider/DynamicDataSourceProvider.class | Bin 0 -> 300 bytes .../YmlDynamicDataSourceProvider.class | Bin 0 -> 3076 bytes .../DefaultDynamicDataSourceSpelParser.class | Bin 0 -> 3010 bytes ...DefaultDynamicDataSourceSpelResolver.class | Bin 0 -> 633 bytes .../spel/DynamicDataSourceSpelParser.class | Bin 0 -> 273 bytes .../spel/DynamicDataSourceSpelResolver.class | Bin 0 -> 235 bytes .../autoconfigure/DataSourceProperty.class | Bin 0 -> 6380 bytes .../DynamicDataSourceAutoConfiguration.class | Bin 0 -> 6116 bytes .../DynamicDataSourceProperties.class | Bin 0 -> 3932 bytes .../autoconfigure/druid/DruidConfig.class | Bin 0 -> 27451 bytes .../DruidDynamicDataSourceConfiguration.class | Bin 0 -> 1261 bytes .../strategy/DynamicDataSourceStrategy.class | Bin 0 -> 332 bytes ...LoadBalanceDynamicDataSourceStrategy.class | Bin 0 -> 1180 bytes .../RandomDynamicDataSourceStrategy.class | Bin 0 -> 1078 bytes .../DynamicDataSourceContextHolder$1.class | Bin 0 -> 692 bytes .../DynamicDataSourceContextHolder.class | Bin 0 -> 1682 bytes ...c-datasource-spring-boot-starter-2.3.7.jar | Bin 0 -> 47180 bytes target/maven-archiver/pom.properties | 5 + .../compile/default-compile/createdFiles.lst | 26 + .../compile/default-compile/inputFiles.lst | 24 + 136 files changed, 5583 insertions(+) create mode 100644 .gitee/ISSUE_TEMPLATE.zh-CN.md create mode 100644 .gitee/PULL_REQUEST_TEMPLATE.zh-CN.md create mode 100644 .github/ISSUE_TEMPLATE create mode 100644 .github/PULL_REQUEST_TEMPLATE create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 CHANGELOG.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 license.txt create mode 100644 pom.xml create mode 100644 samples/.gitignore create mode 100644 samples/README.md create mode 100644 samples/dynamic-druid-mybatis-sample/pom.xml create mode 100644 samples/dynamic-druid-mybatis-sample/src/main/java/com/baomidou/samples/druid/mybatis/Application.java create mode 100644 samples/dynamic-druid-mybatis-sample/src/main/java/com/baomidou/samples/druid/mybatis/entity/User.java create mode 100644 samples/dynamic-druid-mybatis-sample/src/main/java/com/baomidou/samples/druid/mybatis/mapper/UserMapper.java create mode 100644 samples/dynamic-druid-mybatis-sample/src/main/java/com/baomidou/samples/druid/mybatis/service/UserService.java create mode 100644 samples/dynamic-druid-mybatis-sample/src/main/java/com/baomidou/samples/druid/mybatis/service/impl/UserServiceImpl.java create mode 100644 samples/dynamic-druid-mybatis-sample/src/main/resources/application.yml create mode 100644 samples/dynamic-druid-mybatis-sample/src/main/resources/spy.properties create mode 100644 samples/dynamic-druid-mybatis-sample/src/test/java/com/baomidou/samples/druid/mybatis/test/ApplicationTest.java create mode 100644 samples/dynamic-jdbc-template-sample/pom.xml create mode 100644 samples/dynamic-jdbc-template-sample/src/main/java/com/baomidou/samples/jdbc/Application.java create mode 100644 samples/dynamic-jdbc-template-sample/src/main/java/com/baomidou/samples/jdbc/entity/User.java create mode 100644 samples/dynamic-jdbc-template-sample/src/main/java/com/baomidou/samples/jdbc/service/UserService.java create mode 100644 samples/dynamic-jdbc-template-sample/src/main/java/com/baomidou/samples/jdbc/service/impl/UserServiceImpl.java create mode 100644 samples/dynamic-jdbc-template-sample/src/main/resources/application.yml create mode 100644 samples/dynamic-jdbc-template-sample/src/test/java/com/baomidou/samples/jdbc/test/ApplicationTest.java create mode 100644 samples/dynamic-mybatis-sample/pom.xml create mode 100644 samples/dynamic-mybatis-sample/src/main/java/com/baomidou/samples/mybatis/Application.java create mode 100644 samples/dynamic-mybatis-sample/src/main/java/com/baomidou/samples/mybatis/entity/User.java create mode 100644 samples/dynamic-mybatis-sample/src/main/java/com/baomidou/samples/mybatis/mapper/UserMapper.java create mode 100644 samples/dynamic-mybatis-sample/src/main/java/com/baomidou/samples/mybatis/service/UserService.java create mode 100644 samples/dynamic-mybatis-sample/src/main/java/com/baomidou/samples/mybatis/service/impl/UserServiceImpl.java create mode 100644 samples/dynamic-mybatis-sample/src/main/resources/application.yml create mode 100644 samples/dynamic-mybatis-sample/src/test/java/com/baomidou/samples/mybatis/test/ApplicationTest.java create mode 100644 samples/dynamic-mybatis-sample/transaction-logs/tmlog49.log create mode 100644 samples/dynamic-mybatisplus2-sample/pom.xml create mode 100644 samples/dynamic-mybatisplus2-sample/src/main/java/com/baomidou/samples/mybatisplus2/Application.java create mode 100644 samples/dynamic-mybatisplus2-sample/src/main/java/com/baomidou/samples/mybatisplus2/entity/User.java create mode 100644 samples/dynamic-mybatisplus2-sample/src/main/java/com/baomidou/samples/mybatisplus2/mapper/UserMapper.java create mode 100644 samples/dynamic-mybatisplus2-sample/src/main/java/com/baomidou/samples/mybatisplus2/service/UserService.java create mode 100644 samples/dynamic-mybatisplus2-sample/src/main/java/com/baomidou/samples/mybatisplus2/service/impl/UserServiceImpl.java create mode 100644 samples/dynamic-mybatisplus2-sample/src/main/resources/application.yml create mode 100644 samples/dynamic-mybatisplus2-sample/src/test/java/com/baomidou/samples/mybatisplus2/test/ApplicationTest.java create mode 100644 samples/dynamic-mybatisplus3-sample/pom.xml create mode 100644 samples/dynamic-mybatisplus3-sample/src/main/java/com/baomidou/samples/mybatisplus/Application.java create mode 100644 samples/dynamic-mybatisplus3-sample/src/main/java/com/baomidou/samples/mybatisplus/entity/User.java create mode 100644 samples/dynamic-mybatisplus3-sample/src/main/java/com/baomidou/samples/mybatisplus/mapper/UserMapper.java create mode 100644 samples/dynamic-mybatisplus3-sample/src/main/java/com/baomidou/samples/mybatisplus/service/UserService.java create mode 100644 samples/dynamic-mybatisplus3-sample/src/main/java/com/baomidou/samples/mybatisplus/service/impl/UserServiceImpl.java create mode 100644 samples/dynamic-mybatisplus3-sample/src/main/resources/application.yml create mode 100644 samples/dynamic-mybatisplus3-sample/src/test/java/com/baomidou/samples/mybatisplus3/test/ApplicationTest.java create mode 100644 samples/dynamic-nest-sample/pom.xml create mode 100644 samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/Application.java create mode 100644 samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/CreateDatabaseOnStarted.java create mode 100644 samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/controller/TestController.java create mode 100644 samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/entiry/Student.java create mode 100644 samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/entiry/Teacher.java create mode 100644 samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/mapper/StudentMapper.java create mode 100644 samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/mapper/TeacherMapper.java create mode 100644 samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/service/SchoolService.java create mode 100644 samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/service/StudentService.java create mode 100644 samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/service/TeacherService.java create mode 100644 samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/service/impl/SchoolServiceImpl.java create mode 100644 samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/service/impl/StudentServiceImpl.java create mode 100644 samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/service/impl/TeacherServiceImpl.java create mode 100644 samples/dynamic-nest-sample/src/main/resources/application.yml create mode 100644 samples/dynamic-nest-sample/src/test/java/com/baomidou/samples/nest/test/ApplicationTest.java create mode 100644 samples/dynamic-spel-sample/pom.xml create mode 100644 samples/dynamic-spel-sample/src/main/java/com/baomidou/samples/spel/Application.java create mode 100644 samples/dynamic-spel-sample/src/main/java/com/baomidou/samples/spel/controller/UserController.java create mode 100644 samples/dynamic-spel-sample/src/main/java/com/baomidou/samples/spel/entity/User.java create mode 100644 samples/dynamic-spel-sample/src/main/java/com/baomidou/samples/spel/mapper/UserMapper.java create mode 100644 samples/dynamic-spel-sample/src/main/java/com/baomidou/samples/spel/service/UserService.java create mode 100644 samples/dynamic-spel-sample/src/main/java/com/baomidou/samples/spel/service/impl/UserServiceImpl.java create mode 100644 samples/dynamic-spel-sample/src/main/resources/application.yml create mode 100644 samples/dynamic-spel-sample/src/test/java/com/baomidou/samples/spel/test/ApplicationTest.java create mode 100644 samples/pom.xml create mode 100644 src/main/java/com/baomidou/dynamic/datasource/AbstractRoutingDataSource.java create mode 100644 src/main/java/com/baomidou/dynamic/datasource/DynamicDataSourceClassResolver.java create mode 100644 src/main/java/com/baomidou/dynamic/datasource/DynamicDataSourceCreator.java create mode 100644 src/main/java/com/baomidou/dynamic/datasource/DynamicGroupDataSource.java create mode 100644 src/main/java/com/baomidou/dynamic/datasource/DynamicRoutingDataSource.java create mode 100644 src/main/java/com/baomidou/dynamic/datasource/annotation/DS.java create mode 100644 src/main/java/com/baomidou/dynamic/datasource/aop/DynamicDataSourceAnnotationAdvisor.java create mode 100644 src/main/java/com/baomidou/dynamic/datasource/aop/DynamicDataSourceAnnotationInterceptor.java create mode 100644 src/main/java/com/baomidou/dynamic/datasource/provider/AbstractJdbcDataSourceProvider.java create mode 100644 src/main/java/com/baomidou/dynamic/datasource/provider/DynamicDataSourceProvider.java create mode 100644 src/main/java/com/baomidou/dynamic/datasource/provider/YmlDynamicDataSourceProvider.java create mode 100644 src/main/java/com/baomidou/dynamic/datasource/spel/DefaultDynamicDataSourceSpelParser.java create mode 100644 src/main/java/com/baomidou/dynamic/datasource/spel/DefaultDynamicDataSourceSpelResolver.java create mode 100644 src/main/java/com/baomidou/dynamic/datasource/spel/DynamicDataSourceSpelParser.java create mode 100644 src/main/java/com/baomidou/dynamic/datasource/spel/DynamicDataSourceSpelResolver.java create mode 100644 src/main/java/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/DataSourceProperty.java create mode 100644 src/main/java/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/DynamicDataSourceAutoConfiguration.java create mode 100644 src/main/java/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/DynamicDataSourceProperties.java create mode 100644 src/main/java/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/druid/DruidConfig.java create mode 100644 src/main/java/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/druid/DruidDynamicDataSourceConfiguration.java create mode 100644 src/main/java/com/baomidou/dynamic/datasource/strategy/DynamicDataSourceStrategy.java create mode 100644 src/main/java/com/baomidou/dynamic/datasource/strategy/LoadBalanceDynamicDataSourceStrategy.java create mode 100644 src/main/java/com/baomidou/dynamic/datasource/strategy/RandomDynamicDataSourceStrategy.java create mode 100644 src/main/java/com/baomidou/dynamic/datasource/toolkit/DynamicDataSourceContextHolder.java create mode 100644 src/main/resources/META-INF/spring.factories create mode 100644 target/classes/META-INF/spring-configuration-metadata.json create mode 100644 target/classes/META-INF/spring.factories create mode 100644 target/classes/com/baomidou/dynamic/datasource/AbstractRoutingDataSource.class create mode 100644 target/classes/com/baomidou/dynamic/datasource/DynamicDataSourceClassResolver.class create mode 100644 target/classes/com/baomidou/dynamic/datasource/DynamicDataSourceCreator.class create mode 100644 target/classes/com/baomidou/dynamic/datasource/DynamicGroupDataSource.class create mode 100644 target/classes/com/baomidou/dynamic/datasource/DynamicRoutingDataSource.class create mode 100644 target/classes/com/baomidou/dynamic/datasource/annotation/DS.class create mode 100644 target/classes/com/baomidou/dynamic/datasource/aop/DynamicDataSourceAnnotationAdvisor.class create mode 100644 target/classes/com/baomidou/dynamic/datasource/aop/DynamicDataSourceAnnotationInterceptor.class create mode 100644 target/classes/com/baomidou/dynamic/datasource/provider/AbstractJdbcDataSourceProvider.class create mode 100644 target/classes/com/baomidou/dynamic/datasource/provider/DynamicDataSourceProvider.class create mode 100644 target/classes/com/baomidou/dynamic/datasource/provider/YmlDynamicDataSourceProvider.class create mode 100644 target/classes/com/baomidou/dynamic/datasource/spel/DefaultDynamicDataSourceSpelParser.class create mode 100644 target/classes/com/baomidou/dynamic/datasource/spel/DefaultDynamicDataSourceSpelResolver.class create mode 100644 target/classes/com/baomidou/dynamic/datasource/spel/DynamicDataSourceSpelParser.class create mode 100644 target/classes/com/baomidou/dynamic/datasource/spel/DynamicDataSourceSpelResolver.class create mode 100644 target/classes/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/DataSourceProperty.class create mode 100644 target/classes/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/DynamicDataSourceAutoConfiguration.class create mode 100644 target/classes/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/DynamicDataSourceProperties.class create mode 100644 target/classes/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/druid/DruidConfig.class create mode 100644 target/classes/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/druid/DruidDynamicDataSourceConfiguration.class create mode 100644 target/classes/com/baomidou/dynamic/datasource/strategy/DynamicDataSourceStrategy.class create mode 100644 target/classes/com/baomidou/dynamic/datasource/strategy/LoadBalanceDynamicDataSourceStrategy.class create mode 100644 target/classes/com/baomidou/dynamic/datasource/strategy/RandomDynamicDataSourceStrategy.class create mode 100644 target/classes/com/baomidou/dynamic/datasource/toolkit/DynamicDataSourceContextHolder$1.class create mode 100644 target/classes/com/baomidou/dynamic/datasource/toolkit/DynamicDataSourceContextHolder.class create mode 100644 target/dynamic-datasource-spring-boot-starter-2.3.7.jar create mode 100644 target/maven-archiver/pom.properties create mode 100644 target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst create mode 100644 target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst diff --git a/.gitee/ISSUE_TEMPLATE.zh-CN.md b/.gitee/ISSUE_TEMPLATE.zh-CN.md new file mode 100644 index 0000000..cacdba4 --- /dev/null +++ b/.gitee/ISSUE_TEMPLATE.zh-CN.md @@ -0,0 +1,29 @@ +# 环境 + +您所使用的 JDK版本(必填): + +您所使用的 SpringBoot版本(必填): + +您所使用的 Starter版本(必填): + +# 描述 + + + + + +期望值: + +实际值: + +# 重现步骤 + +- 步骤1 + + +- 步骤2 + + +- 步骤3 + + diff --git a/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md b/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md new file mode 100644 index 0000000..a285c0d --- /dev/null +++ b/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md @@ -0,0 +1,16 @@ +**What kind of change does this PR introduce?** (check at least one) + +- [ ] Bugfix +- [ ] Feature +- [ ] Code style +- [ ] Refactor +- [ ] Doc +- [ ] Other, please describe: + +**The description of the PR:** + + +**Other information:** + + + diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE new file mode 100644 index 0000000..56184fd --- /dev/null +++ b/.github/ISSUE_TEMPLATE @@ -0,0 +1,29 @@ +# Enviroment + +JDK Version(required): + +SpringBoot Version(required): + +Starter Version(required): + +# Describe + + + + + +Expected Result: + +Actual Result: + +# Steps to reproduce + +- Step 1 + + +- Step 2 + + +- Step 3 + + diff --git a/.github/PULL_REQUEST_TEMPLATE b/.github/PULL_REQUEST_TEMPLATE new file mode 100644 index 0000000..a285c0d --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE @@ -0,0 +1,16 @@ +**What kind of change does this PR introduce?** (check at least one) + +- [ ] Bugfix +- [ ] Feature +- [ ] Code style +- [ ] Refactor +- [ ] Doc +- [ ] Other, please describe: + +**The description of the PR:** + + +**Other information:** + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..19240d3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +*/target/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/build/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..739aeec --- /dev/null +++ b/.travis.yml @@ -0,0 +1,12 @@ +language: java + +jdk: +- openjdk7 +- openjdk8 + +after_success: +- echo success + +cache: + directories: + - $HOME/.m2 \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..90ed5f4 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,122 @@ +# v2.4.0 +- 重构了druid配置。 +- 支持了更多druid参数配置,支持了加密。 +- 完善了文档。 + +# v2.3.6 +- 修复上个版本的druid参数设置bug。 +- 对外开放获取所有数据源和所有组数据源方法。 + +# v2.3.5 druid参数设置有bug +- 支持p6sy。(格式化sql利器) +- 支持jndi数据源。 +- 支持druid更多参数。 +- 支持hikari参数设置。 +- 切换数据源工具类只有在clear的时候才移除当前数据源名称。 +- 启动带上数据源名称。 +- 添加了更多的测试代码。 + +# v2.3.4 +- 底层细节优化。 +- 重构多级数据源切换。 +- 示例项目重构。 + +# v2.3.3 问题版本-不能使用 +- 支持嵌套下多级的数据源切换(service1 mysql调用service2 oracle)。 + +https://gitee.com/baomidou/dynamic-datasource-spring-boot-starter/issues/IO33C + +- 修复spel对request和session的支持。 + +# v2.3.2 问题版本-不能使用 +- 修复在不需要session的场景中自动注入session。 + +# v2.3.1 问题版本-不能使用 +- 修复2.3.0中使用spel session 和header的取值错误。 + +# v.2.3.0 + +- 重构创建数据源类。废弃DataSourceFactory,改为Bean的DynamicDataSourceCreator。 +- 自动适配mybatisPlus,移除参数的mp-enabled。 +- 新特性支持spel参数获取数据源。 + +# v2.2.3 + +- 支持druid参数全局配置。 +- 对外暴露动态添加删除数据源的方法。 +- 增加在组内数据源为空时使用默认数据源。 +- 去除启动时校验组内只有单个数据源。 + +# v2.2.2 问题版本-不能使用 + +- 修复上个版本mp3适配失败的Bug。 + +# v2.2.1 + +- 适配mybatis2.x版本。 + +# v2.2.0 + +- 修复从默认数据源获取数据不能是组数据源的bug。 +- 摒弃spring原生动态数据源抽象,重新实现。 +- 去除底层方法缓存。 +- 提供从JDBC中获取数据源的抽象。 +- 部分代码重新划分包。 +- license的组织名更新成为baomidou。 + +# v2.1.1 + +- 切面顺序调整为最高。 +- 底层切换数据源逻辑优化。 + +# v2.1.0 + +- 修复了底层一个逻辑bug。 +- 提供了对mp的原生支持。 +- 底层代码进行了细微的性能优化。 + +# v2.0.2 + +- 修复springboot2.0以上版本不能设置HikariDataSource。 +- 底层代码的整理。 +- Druid数据源初始大小改为3。 + +# v2.0.1 + +- 修复一个方法缓存的bug,会引起同名方法的注解失效。 +- 底层代码的重命名和部分格式的调整。 + +# v2.0.0 + +- Breaking change:数据源配置同级,不再默认主从,支持多种方案。 +- Breaking change:使用约定大于配置,开启 多组模式 新启程。 +- Breaking change:注解包路径变更,与苞米豆其他项目保持一致写法。 +- Breaking change:不再支持@DS空注解。 +- Breaking change:不再支持强制主库force-master配置。 +- Breaking change:数据源选择策略现在如需更改需要设置配置文件的dynamicDataSourceStrategyClass。 +- Druid数据源默认validation-query 为select 1 。 +- 全部源码改为中文。 +- 增加了部分启动时和运行中的日志。 + +# v1.4.0 + +- 支持了在类上注解,如果方法上同时有注解则方法注解优先。 +- 支持了遇到事物强制主库,并且是默认行为,可在配置更改foeceMaster。 +- 最低支持jdk1.7,springboot1.4.x。 +- 重构aop,解决了部分springboot版本引入插件无效的问题。 + +# v1.3.0 + +- 对Druid的paCache属性提供支持。 +- 还原上一版本切面的配置方式。 +- 其他一些细节的优化。 + +# v1.2.0 + +- 对Druid提供更完善的支持。 +- 更改了默认的注解切面的注入方式。 +- 抽象了切面选择数据源接口,方便以后支持spel语法等。 + +# v1.1.0 + +- 支持Druid数据源 (support DruidDataSource)。 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..00a1db2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "{}" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright 2018 苞米豆 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..442d856 --- /dev/null +++ b/README.md @@ -0,0 +1,482 @@ +

+ + + +

+ +

+ 一个基于springboot的快速集成多数据源的启动器 +

+ +

+ + + + + + + + + + + + + + +

+ +#### [Github](https://github.com/baomidou/dynamic-datasource-spring-boot-starter) | [码云Gitee](https://gitee.com/baomidou/dynamic-datasource-spring-boot-starter) + +# 简介 + +dynamic-datasource-spring-boot-starter 是一个基于springboot的快速集成多数据源的启动器。 + +其支持 **Jdk 1.7+, SpringBoot 1.4.x 1.5.x 2.0.x**。最新版为 + +**示例项目** 可参考项目下的samples目录。 + +**示例项目** 可参考项目下的samples目录。 + +**示例项目** 可参考项目下的samples目录。 + +# 优势 + +网上关于动态数据源的切换的文档有很多,核心只有两种。1是构建多套环境,2是基于spring原生的 `AbstractRoutingDataSource` 切换。 + +如果你的数据源较少,场景不复杂,选择以上任意一种都可以。如果你需要更多特性,请试着尝试本数据源。 + +1. 数据源分组,适用于多种场景 纯粹多库 读写分离 一主多从 混合模式。 +2. 简单集成Druid数据源监控多数据源,简单集成Mybatis-Plus简化单表,简单集成P6sy格式化sql,简单集成Jndi数据源。 +3. 自定义数据源来源。 +4. 动态增减数据源。 +5. 使用spel动态参数解析数据源,如从session,header和参数中获取数据源。(多租户架构神器) +6. 多层数据源嵌套切换。(一个业务ServiceA调用ServiceB,ServiceB调用ServiceC,每个Service都是不同的数据源) + +# 劣势 + +不能使用多数据源事物(同一个数据源下能使用事物),网上其他方案也都不能提供。 + +如果你需要使用到分布式事物,那么你的架构应该到了微服务化的时候了。 + +如果呼声强烈,项目达到800 star,作者考虑集成分布式事物。 + +PS: 如果您只是几个数据库但是有强烈的需求分布式事物,建议还是使用传统方式自己构建多套环境集成atomic这类,网上百度很多。 + +# 约定 + +1. 本框架只做 **切换数据源** 这件核心的事情,并**不限制你的具体操作**,切换了数据源可以做任何CRUD。 +2. 配置文件所有以下划线 `_` 分割的数据源 **首部** 即为组的名称,相同组名称的数据源会放在一个组下。 +3. 切换数据源即可是组名,也可是具体数据源名称,切换时默认采用负载均衡机制切换。 +4. 默认的数据源名称为 **master** ,你可以通过spring.datasource.dynamic.primary修改。 +5. 方法上的注解优先于类上注解。 + +# 建议 + +强烈建议在 **主从模式** 下遵循普遍的规则,以便他人能更轻易理解你的代码。 + +主数据库 **建议** 只执行 `INSERT` `UPDATE` `DELETE` 操作。 + +从数据库 **建议** 只执行 `SELECT` 操作。 + +# 使用方法 + +1. 引入dynamic-datasource-spring-boot-starter。 + +```xml + + com.baomidou + dynamic-datasource-spring-boot-starter + ${version} + +``` +2. 配置数据源。 + +```yaml +spring: + datasource: + dynamic: + primary: master #设置默认的数据源或者数据源组,默认值即为master + datasource: + master: + username: root + password: 123456 + driver-class-name: com.mysql.jdbc.Driver + url: jdbc:mysql://xx.xx.xx.xx:3306/dynamic + slave_1: + username: root + password: 123456 + driver-class-name: com.mysql.jdbc.Driver + url: jdbc:mysql://xx.xx.xx.xx:3307/dynamic + slave_2: + username: root + password: 123456 + driver-class-name: com.mysql.jdbc.Driver + url: jdbc:mysql://xx.xx.xx.xx:3308/dynamic + #......省略 + #以上会配置一个默认库master,一个组slave下有两个子库slave_1,slave_2 +``` + +```yaml +# 多主多从 纯粹多库(记得设置primary) 混合配置 +spring: spring: spring: + datasource: datasource: datasource: + dynamic: dynamic: dynamic: + datasource: datasource: datasource: + master_1: mysql: master: + master_2: oracle: slave_1: + slave_1: sqlserver: slave_2: + slave_2: postgresql: oracle_1: + slave_3: h2: oracle_2: +``` + +3. 使用 **@DS** 切换数据源。 + +**@DS** 可以注解在方法上和类上,**同时存在方法注解优先于类上注解**。 + +注解在service实现或mapper接口方法上,但强烈不建议同时在service和mapper注解。 (可能会有问题) + +| 注解 | 结果 | +| :-----------: | :--------------------------------------: | +| 没有@DS | 默认数据源 | +| @DS("dsName") | dsName可以为组名也可以为具体某个库的名称 | + +```java +@Service +@DS("slave") +public class UserServiceImpl implements UserService { + + @Autowired + private JdbcTemplate jdbcTemplate; + + public List> selectAll() { + return jdbcTemplate.queryForList("select * from user"); + } + + @Override + @DS("slave_1") + public List> selectByCondition() { + return jdbcTemplate.queryForList("select * from user where age >10"); + } +} +``` +在mybatis环境下也可注解在mapper接口层。 + +```java +@DS("slave") +public interface UserMapper { + + @Insert("INSERT INTO user (name,age) values (#{name},#{age})") + boolean addUser(@Param("name") String name, @Param("age") Integer age); + + @Update("UPDATE user set name=#{name}, age=#{age} where id =#{id}") + boolean updateUser(@Param("id") Integer id, @Param("name") String name, @Param("age") Integer age); + + @Delete("DELETE from user where id =#{id}") + boolean deleteUser(@Param("id") Integer id); + + @Select("SELECT * FROM user") + @DS("slave_1") + List selectAll(); +} +``` + +--- + +大部分用户看到这里就可以了,赶紧体验一下吧! 如果需要更多功能请继续往下看。 + +--- + +# 第三方集成 + +## 集成 Druid + +springBoot2.x默认使用HikariCP,但在国内Druid的使用者非常庞大,此项目特地对其进行了适配,完成多数据源下使用Druid进行监控。 + +> 注意:主从可以使用不同的数据库连接池,如**master使用Druid监控,从库使用HikariCP**。 如果不配置连接池type类型,默认是Druid优先于HikariCP。 + +1. 项目引入`druid-spring-boot-starter`依赖。 + +```xml + + com.alibaba + druid-spring-boot-starter + 1.1.10 + +``` + +2. **排除** 原生Druid的快速配置类。 + +```java +@SpringBootApplication(exclude = DruidDataSourceAutoConfigure.class) +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} +``` + +```yaml +#如果遇到DruidDataSourceAutoConfigure抛出no suitable driver表示注解排除没有生效尝试以下这种排除方法 +spring: + autoconfigure: + exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure +``` + +> 为什么要排除DruidDataSourceAutoConfigure ? +> +> DruidDataSourceAutoConfigure会注入一个DataSourceWrapper,其会在原生的spring.datasource下找url,username,password等。而我们动态数据源的配置路径是变化的。 + +3. 其他属性依旧如原生`druid-spring-boot-starter`的配置。 + +```yaml +spring: + datasource: + druid: + stat-view-servlet: + loginUsername: admin + loginPassword: 123456 + dynamic: + druid: # 全局druid参数,绝大部分值和默认保持一致。(现已支持的参数如下,不清楚含义不要乱设置) + initial-size: + max-active: + min-idle: + max-wait: + time-between-eviction-runs-millis: + time-between-log-stats-millis: + stat-sqlmax-size: + min-evictable-idle-time-millis: + max-evictable-idle-time-millis: + test-while-idle: + test-on-borrow: + test-on-return: + validation-query: + validation-query-timeout: + use-global-datasource-stat: + async-init: + clear-filters-enable: + reset-stat-enable: + not-full-timeout-retry-count: + max-wait-thread-count: + fail-fast: + phyTimeout-millis: + keep-alive: + pool-prepared-statements: + init-variants: + init-global-variants: + use-unfair-lock: + kill-when-socket-read-timeout: + connection-properties: + max-pool-prepared-statement-per-connection-size: + init-connection-sqls: + share-prepared-statements: + connection-errorretry-attempts: + break-after-acquire-failure: + filters: stat,wall # 注意这个值和druid原生不一致,默认启动了stat,wall + datasource: + master: + username: root + password: 123456 + driver-class-name: com.mysql.jdbc.Driver + url: jdbc:mysql://47.100.20.186:3306/dynamic?characterEncoding=utf8&useSSL=false + druid: # 以下参数针对每个库可以重新设置druid参数 + initial-size: + validation-query: select 1 FROM DUAL #比如oracle就需要重新设置这个 + public-key: #(非全局参数)设置即表示启用加密,底层会自动帮你配置相关的连接参数和filter。 +# ...... + + +# 生成 publickey 和密码 +# java -cp druid-1.1.10.jar com.alibaba.druid.filter.config.ConfigTools youpassword +``` + +如上即可配置访问用户和密码,访问 http://localhost:8080/druid/index.html 查看druid监控。 + +## 集成 MybatisPlus + +只要进入mybatisPlus相关jar包,项目自动集成。 兼容mybatisPlus 2.x和3.x的版本。 + +只要注解在mybatisPlus的mapper或serviceImpl上即可完成mp内置方法切换。 + +```java +// 注解在类上统一切换数据源,如果想某个方法特殊处理,请自己用一个方法包裹然后注解在该方法上。 +@DS("slave") +public interface UserMapper extends BaseMapper { +} + +@Service +@DS("slave")//再次建议不要同时注解在service和mapper上。 +public class UserServiceImpl extends ServiceImpl implements UserService { + +} +``` + +## 集成 P6sy + +p6sy大部分人最常用的功能就是格式化你的sql语句。 + +```mysql +# 如在使用mybatis的过程中,原生输出的语句是带?号的。在需要复制到其他地方执行看效果的时候很不方便。 +select * from user where age>? +# 在使用了p6sy后,其会帮你格式化成真正的执行语句。 +select * from user where age>6 +``` + +1. 引入相关jar包。 + +```xml + + p6spy + p6spy + 3.8.0 + +``` + +2. 启用配置。 + +```yaml +spring: + datasource: + dynamic: + p6sy: true # 默认false,建议线上关闭。 +``` + +3. 引入相关配置文件。 + +在classPath下创建spy.properties + +```properties +# 一个最简单配置,定义slf4j日志输出。 更多参数请自行了解。 +appender=com.p6spy.engine.spy.appender.Slf4JLogger +``` + +## 集成 Jndi数据源 + +在某些场景我们的数据源来源于jndi,开启使用十分方便。 + +```yaml +spring: + datasource: + dynamic: + master: + jndi_name: xxx #只要配置即表示启用。 +``` + +# 高级 + +## 自定义数据源来源 + +数据源来源的默认实现是`YmlDynamicDataSourceProvider`,其从yaml或properties中读取信息并解析出所有数据源信息。 + +```java +public interface DynamicDataSourceProvider { + + /** + * 加载所有数据源 + * + * @return 所有数据源,key为数据源名称 + */ + Map loadDataSources(); + +} +``` + +可以参考 `AbstractJdbcDataSourceProvider` (仅供参考,不能直接使用)来实现从JDBC数据库中获取数据库连接信息。 + +## 动态增减数据源 + +```java +public class DemoController { + + @Autowired //在需要的地方注入,然后调用相关方法增减数据源 + private DynamicRoutingDataSource dynamicRoutingDataSource; + + //public synchronized void addDataSource(String ds, DataSource dataSource) + + //public synchronized void removeDataSource(String ds) + + // 另外这个类也有相关方法获取所有数据源的信息 +} +``` + +## 解析自己的数据源 + +知道了如何动态增减数据源,但不知道如何解析自己数据源,来动态创建一个DataSource。 + +`com.baomidou.dynamic.datasource.DynamicDataSourceCreator` 是一个数据源创建BEAN。其对spring1.x和2.x做了一些适配。 + +熟悉spring的朋友也可以直接调用原生的DataSourceBuilder来创建简单数据源。 + +```java +//核心源码简述 +public class DynamicDataSourceCreator { + +//最外层创建数据源方法,一般直接调用这个就可以 +public DataSource createDataSource(DataSourceProperty dataSourceProperty) + +//项目只对druid和hikari做了特殊处理,支持一些参数和配置,其他的类型只能调用这个 +public DataSource createBasicDataSource(DataSourceProperty dataSourceProperty) + +//创建jndi数据源,只要配置参数设置了jndiName就会创建,优先级最高 +public DataSource createJNDIDataSource(String jndiName) + +//创建druid数据源,如果未指定type,druid的优先于hikari +public DataSource createDruidDataSource(DataSourceProperty dataSourceProperty) + +//创建hikari数据源 +public DataSource createHikariDataSource(DataSourceProperty dataSourceProperty) + +} +``` + +## 动态参数解析数据源(spel) + +所有以 `#` 开头的参数都会使用spel从参数中获取数据源。 + +```java +@DS("#session.tenantName")//从session获取 +public List selectSpelBySession() { + return userMapper.selectUsers(); +} + +@DS("#header.tenantName")//从header获取 +public List selectSpelByHeader() { + return userMapper.selectUsers(); +} + +@DS("#tenantName")//使用spel从参数获取 +public List selectSpelByKey(String tenantName) { + return userMapper.selectUsers(); +} + +@DS("#user.tenantName")//使用spel从复杂参数获取 +public List selecSpelByTenant(User user) { + return userMapper.selectUsers(); +} +``` +如果你还想对spel解析的参数进行进一步处理,请注入`DynamicDataSourceSpelResolver`。 + +默认的`DefaultDynamicDataSourceSpelResolver` 没有对解析到的参数进行处理直接返回。 + +# 苞米豆开源项目 + + mybatis单表快速开发,3.0已支持lamdba写法 。 + + 基于cookie的SSO中间件。 + + 多数据源,动态数据源,主从分离 快速启动器。 + + 谷歌验证码 快速启动器。 + + 支持RedisTemplate、Redisson、Zookeeper 简单可靠的分布式锁 快速启动器。 + +# 常见问题 + +1. 多个库的事物如何处理? + +**不能 不能 不能**,一个业务操作涉及多个库不要加事物。 + +2. 是否支持JPA? + +不完全支持,受限于JPA底层,你只能在一个controller下切换第一个库,第二个库不能切换。(如有解决办法请联系作者) diff --git a/license.txt b/license.txt new file mode 100644 index 0000000..8470bb2 --- /dev/null +++ b/license.txt @@ -0,0 +1,14 @@ +Copyright © ${project.inceptionYear} organization baomidou +
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..55f1729
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,210 @@
+
+
+    4.0.0
+
+    
+        org.springframework.boot
+        spring-boot-starter-parent
+        1.5.1.RELEASE
+        
+    
+
+    com.baomidou
+    dynamic-datasource-spring-boot-starter
+    2.4.0
+    jar
+
+    dynamic-datasource-spring-boot-starter
+    dynamic datasource with annotation
+    2018
+
+    
+        
+            Apache License, Version 2.0
+            http://www.apache.org/licenses/LICENSE-2.0
+        
+    
+
+    
+        baomidou
+        https://gitee.com/baomidou
+    
+
+    
+        
+            TaoYu
+            tracy5546@gmail.com
+        
+        
+            KanYuXia
+            kanyuxia@outlook.com
+        
+    
+
+    
+        https://github.com/baomidou/dynamic-datasource-spring-boot-starter
+        scm:git:https://github.com/baomidou/dynamic-datasource-spring-boot-starter.git
+        scm:git:https://github.com/baomidou/dynamic-datasource-spring-boot-starter.git
+        
+        HEAD
+    
+
+    
+        UTF-8
+        UTF-8
+        1.7
+    
+
+    
+        
+            org.springframework.boot
+            spring-boot-starter-jdbc
+        
+        
+            org.springframework.boot
+            spring-boot-starter-aop
+        
+        
+            org.springframework.boot
+            spring-boot-starter-web
+            true
+        
+        
+            com.alibaba
+            druid-spring-boot-starter
+            1.1.10
+            true
+        
+        
+            com.baomidou
+            mybatis-plus-boot-starter
+            3.0.5
+            true
+        
+        
+            com.zaxxer
+            HikariCP
+            2.7.0
+            true
+        
+        
+            org.springframework.boot
+            spring-boot-configuration-processor
+            true
+        
+        
+            org.projectlombok
+            lombok
+            true
+        
+        
+            p6spy
+            p6spy
+            3.8.0
+            true
+        
+    
+
+    
+        
+            
+                org.apache.maven.plugins
+                maven-compiler-plugin
+                3.8.0
+                
+                    ${java.version}
+                    ${java.version}
+                
+            
+            
+                com.mycila
+                license-maven-plugin
+                3.0
+                
+                    
${project.basedir}/license.txt
+ + src/main/java/** + +
+ + + + check + + + +
+
+
+ + + + release + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-source-plugin + + + package + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.8 + true + + ossrh + https://oss.sonatype.org/ + true + + + + + + + +
diff --git a/samples/.gitignore b/samples/.gitignore new file mode 100644 index 0000000..19240d3 --- /dev/null +++ b/samples/.gitignore @@ -0,0 +1,25 @@ +*/target/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/build/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ \ No newline at end of file diff --git a/samples/README.md b/samples/README.md new file mode 100644 index 0000000..89db2a2 --- /dev/null +++ b/samples/README.md @@ -0,0 +1,13 @@ +# 演示例子 + +所有数据库连接为h2数据库,仅供测试。 + +所有测试可直接跑,注意启动的日志。 + +1. dynamic-jdbc-template-sample 原生jdbcTemplate下使用的示范 +2. dynamic-mybatis-sample 原生mybatis下使用的示范 +3. dynamic-mybatisplus2-sample mybatisPlus2下使用的示范 +4. dynamic-mybatisplus3-sample mybatisPlus3下使用的示范 +5. dynamic-druid-mybatis-sample mybatis和druid下使用的示范 +6. dynamic-spel-sample mybatis和从参数获取数据源下使用的示范(高级)适用于多租户 +7. dynamic-nest-sample 嵌套切换示范 \ No newline at end of file diff --git a/samples/dynamic-druid-mybatis-sample/pom.xml b/samples/dynamic-druid-mybatis-sample/pom.xml new file mode 100644 index 0000000..528d313 --- /dev/null +++ b/samples/dynamic-druid-mybatis-sample/pom.xml @@ -0,0 +1,31 @@ + + + + dynamic-datsource-samples + com.baomidou + 1.0.0 + + 4.0.0 + + dynamic-druid-mybatis-sample + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 1.3.2 + + + com.alibaba + druid-spring-boot-starter + 1.1.10 + + + p6spy + p6spy + 3.8.0 + + + \ No newline at end of file diff --git a/samples/dynamic-druid-mybatis-sample/src/main/java/com/baomidou/samples/druid/mybatis/Application.java b/samples/dynamic-druid-mybatis-sample/src/main/java/com/baomidou/samples/druid/mybatis/Application.java new file mode 100644 index 0000000..a5803f9 --- /dev/null +++ b/samples/dynamic-druid-mybatis-sample/src/main/java/com/baomidou/samples/druid/mybatis/Application.java @@ -0,0 +1,16 @@ +package com.baomidou.samples.druid.mybatis; + +import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication(exclude = DruidDataSourceAutoConfigure.class) +@MapperScan("com.baomidou.samples.druid.mybatis.mapper") +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} \ No newline at end of file diff --git a/samples/dynamic-druid-mybatis-sample/src/main/java/com/baomidou/samples/druid/mybatis/entity/User.java b/samples/dynamic-druid-mybatis-sample/src/main/java/com/baomidou/samples/druid/mybatis/entity/User.java new file mode 100644 index 0000000..76ad779 --- /dev/null +++ b/samples/dynamic-druid-mybatis-sample/src/main/java/com/baomidou/samples/druid/mybatis/entity/User.java @@ -0,0 +1,34 @@ +package com.baomidou.samples.druid.mybatis.entity; + +public class User { + + private Integer id; + + private String name; + + private Integer age; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } +} diff --git a/samples/dynamic-druid-mybatis-sample/src/main/java/com/baomidou/samples/druid/mybatis/mapper/UserMapper.java b/samples/dynamic-druid-mybatis-sample/src/main/java/com/baomidou/samples/druid/mybatis/mapper/UserMapper.java new file mode 100644 index 0000000..e0ae233 --- /dev/null +++ b/samples/dynamic-druid-mybatis-sample/src/main/java/com/baomidou/samples/druid/mybatis/mapper/UserMapper.java @@ -0,0 +1,17 @@ +package com.baomidou.samples.druid.mybatis.mapper; + +import com.baomidou.samples.druid.mybatis.entity.User; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +public interface UserMapper { + + @Insert("INSERT INTO user (name,age) values (#{name},#{age})") + boolean addUser(@Param("name") String name, @Param("age") Integer age); + + @Select("SELECT * FROM user where age > #{age}") + List selectUsers(@Param("age") Integer age); +} diff --git a/samples/dynamic-druid-mybatis-sample/src/main/java/com/baomidou/samples/druid/mybatis/service/UserService.java b/samples/dynamic-druid-mybatis-sample/src/main/java/com/baomidou/samples/druid/mybatis/service/UserService.java new file mode 100644 index 0000000..f189b13 --- /dev/null +++ b/samples/dynamic-druid-mybatis-sample/src/main/java/com/baomidou/samples/druid/mybatis/service/UserService.java @@ -0,0 +1,15 @@ +package com.baomidou.samples.druid.mybatis.service; + + +import com.baomidou.samples.druid.mybatis.entity.User; + +import java.util.List; + +public interface UserService { + + void addUser(User user); + + List selectUsersFromDs(); + + List selectUserFromDsGroup(); +} diff --git a/samples/dynamic-druid-mybatis-sample/src/main/java/com/baomidou/samples/druid/mybatis/service/impl/UserServiceImpl.java b/samples/dynamic-druid-mybatis-sample/src/main/java/com/baomidou/samples/druid/mybatis/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..1773cf1 --- /dev/null +++ b/samples/dynamic-druid-mybatis-sample/src/main/java/com/baomidou/samples/druid/mybatis/service/impl/UserServiceImpl.java @@ -0,0 +1,35 @@ +package com.baomidou.samples.druid.mybatis.service.impl; + + +import com.baomidou.dynamic.datasource.annotation.DS; +import com.baomidou.samples.druid.mybatis.entity.User; +import com.baomidou.samples.druid.mybatis.mapper.UserMapper; +import com.baomidou.samples.druid.mybatis.service.UserService; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.List; + +@Service +public class UserServiceImpl implements UserService { + + @Resource + private UserMapper userMapper; + + @Override + public void addUser(User user) { + userMapper.addUser(user.getName(), user.getAge()); + } + + @DS("slave_1") + @Override + public List selectUsersFromDs() { + return userMapper.selectUsers(1); + } + + @DS("slave") + @Override + public List selectUserFromDsGroup() { + return userMapper.selectUsers(1); + } +} diff --git a/samples/dynamic-druid-mybatis-sample/src/main/resources/application.yml b/samples/dynamic-druid-mybatis-sample/src/main/resources/application.yml new file mode 100644 index 0000000..2520e8d --- /dev/null +++ b/samples/dynamic-druid-mybatis-sample/src/main/resources/application.yml @@ -0,0 +1,69 @@ +spring: + datasource: + dynamic: + p6spy: true + # druid: #以下是全局默认值,可以全局更改 + # initial-size: + # max-active: + # min-idle: + # max-wait: + # time-between-eviction-runs-millis: + # time-between-log-stats-millis: + # stat-sqlmax-size: + # min-evictable-idle-time-millis: + # max-evictable-idle-time-millis: + # test-while-idle: + # test-on-borrow: + # test-on-return: + # validation-query: + # validation-query-timeout: + # use-global-datasource-stat: + # async-init: + # clear-filters-enable: + # reset-stat-enable: + # not-full-timeout-retry-count: + # max-wait-thread-count: + # fail-fast: + # phyTimeout-millis: + # keep-alive: + # pool-prepared-statements: + # init-variants: + # init-global-variants: + # use-unfair-lock: + # kill-when-socket-read-timeout: + # connection-properties: + # max-pool-prepared-statement-per-connection-size: + # init-connection-sqls: + # share-prepared-statements: + # connection-errorretry-attempts: + # break-after-acquire-failure: + # filters: stat,wall # 注意这个值和druid原生不一致,默认启动了stat,wall + datasource: + master: + username: sa + password: "LYIXYTfaq9ug7bqAfcoU1gQovp/Atx7wsdJ5D7oFN5QMqZUI4OGHyMkCA7IxtYAIQr/8cMVFGo9oa/mqtMrzmQ==" + url: jdbc:h2:mem:test + driver-class-name: org.h2.Driver + druid: #这里可以重写默认值 + initial-size: 5 + public-key: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKUVA/IL/iON8f63bv2i/pIAK+1sXY228slLkTKrI9axwBMIoPV7+PqdRTv6uqMl3j6nei0EDBWEu/Wp/qOQ/ScCAwEAAQ== + slave_1: + username: sa + password: "123456" + url: jdbc:h2:mem:test + driver-class-name: org.h2.Driver + druid: + initial-size: 6 + slave_2: + username: sa + password: "123456" + url: jdbc:h2:mem:test + driver-class-name: org.h2.Driver + slave_3: + username: sa + password: "123456" + url: jdbc:h2:mem:test + driver-class-name: org.h2.Driver +logging: + level: + com.baomidou: debug \ No newline at end of file diff --git a/samples/dynamic-druid-mybatis-sample/src/main/resources/spy.properties b/samples/dynamic-druid-mybatis-sample/src/main/resources/spy.properties new file mode 100644 index 0000000..3358547 --- /dev/null +++ b/samples/dynamic-druid-mybatis-sample/src/main/resources/spy.properties @@ -0,0 +1 @@ +appender=com.p6spy.engine.spy.appender.Slf4JLogger \ No newline at end of file diff --git a/samples/dynamic-druid-mybatis-sample/src/test/java/com/baomidou/samples/druid/mybatis/test/ApplicationTest.java b/samples/dynamic-druid-mybatis-sample/src/test/java/com/baomidou/samples/druid/mybatis/test/ApplicationTest.java new file mode 100644 index 0000000..0f2788e --- /dev/null +++ b/samples/dynamic-druid-mybatis-sample/src/test/java/com/baomidou/samples/druid/mybatis/test/ApplicationTest.java @@ -0,0 +1,65 @@ +package com.baomidou.samples.druid.mybatis.test; + +import com.baomidou.samples.druid.mybatis.Application; +import com.baomidou.samples.druid.mybatis.entity.User; +import com.baomidou.samples.druid.mybatis.service.UserService; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Random; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = Application.class) +public class ApplicationTest { + + private Random random = new Random(); + + @Autowired + private UserService userService; + + @Autowired + private DataSource dataSource; + + @Before + public void beforeTest() { + try { + Connection connection = dataSource.getConnection(); + connection.createStatement().execute("CREATE TABLE IF NOT EXISTS USER (\n" + + " id BIGINT(20) NOT NULL AUTO_INCREMENT,\n" + + " name VARCHAR(30) NULL DEFAULT NULL ,\n" + + " age INT(11) NULL DEFAULT NULL ,\n" + + " PRIMARY KEY (id)\n" + + ");"); + connection.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + + @Test + public void addUser() { + User user = new User(); + user.setName("测试用户" + random.nextInt()); + user.setAge(random.nextInt(100)); + userService.addUser(user); + } + + @Test + public void selectUsersFromDs() { + userService.selectUsersFromDs(); + } + + @Test + public void selectUserFromDsGroup() { + userService.selectUserFromDsGroup(); + } + +} \ No newline at end of file diff --git a/samples/dynamic-jdbc-template-sample/pom.xml b/samples/dynamic-jdbc-template-sample/pom.xml new file mode 100644 index 0000000..6feb7ba --- /dev/null +++ b/samples/dynamic-jdbc-template-sample/pom.xml @@ -0,0 +1,20 @@ + + + + dynamic-datsource-samples + com.baomidou + 1.0.0 + + 4.0.0 + + dynamic-jdbc-template-sample + + + + org.springframework.boot + spring-boot-starter-jdbc + + + \ No newline at end of file diff --git a/samples/dynamic-jdbc-template-sample/src/main/java/com/baomidou/samples/jdbc/Application.java b/samples/dynamic-jdbc-template-sample/src/main/java/com/baomidou/samples/jdbc/Application.java new file mode 100644 index 0000000..7cc3883 --- /dev/null +++ b/samples/dynamic-jdbc-template-sample/src/main/java/com/baomidou/samples/jdbc/Application.java @@ -0,0 +1,22 @@ +package com.baomidou.samples.jdbc; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.jdbc.core.JdbcTemplate; + +import javax.sql.DataSource; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + + @Bean + public JdbcTemplate jdbcTemplate(DataSource dataSource) { + return new JdbcTemplate(dataSource); + } + +} \ No newline at end of file diff --git a/samples/dynamic-jdbc-template-sample/src/main/java/com/baomidou/samples/jdbc/entity/User.java b/samples/dynamic-jdbc-template-sample/src/main/java/com/baomidou/samples/jdbc/entity/User.java new file mode 100644 index 0000000..bf2ac67 --- /dev/null +++ b/samples/dynamic-jdbc-template-sample/src/main/java/com/baomidou/samples/jdbc/entity/User.java @@ -0,0 +1,34 @@ +package com.baomidou.samples.jdbc.entity; + +public class User { + + private Integer id; + + private String name; + + private Integer age; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } +} diff --git a/samples/dynamic-jdbc-template-sample/src/main/java/com/baomidou/samples/jdbc/service/UserService.java b/samples/dynamic-jdbc-template-sample/src/main/java/com/baomidou/samples/jdbc/service/UserService.java new file mode 100644 index 0000000..b9730ae --- /dev/null +++ b/samples/dynamic-jdbc-template-sample/src/main/java/com/baomidou/samples/jdbc/service/UserService.java @@ -0,0 +1,14 @@ +package com.baomidou.samples.jdbc.service; + +import com.baomidou.samples.jdbc.entity.User; + +import java.util.List; + +public interface UserService { + + void addUser(User user); + + List selectUsersFromDs(); + + List selectUserFromDsGroup(); +} diff --git a/samples/dynamic-jdbc-template-sample/src/main/java/com/baomidou/samples/jdbc/service/impl/UserServiceImpl.java b/samples/dynamic-jdbc-template-sample/src/main/java/com/baomidou/samples/jdbc/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..08cedd7 --- /dev/null +++ b/samples/dynamic-jdbc-template-sample/src/main/java/com/baomidou/samples/jdbc/service/impl/UserServiceImpl.java @@ -0,0 +1,35 @@ +package com.baomidou.samples.jdbc.service.impl; + + +import com.baomidou.dynamic.datasource.annotation.DS; +import com.baomidou.samples.jdbc.entity.User; +import com.baomidou.samples.jdbc.service.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class UserServiceImpl implements UserService { + + @Autowired + private JdbcTemplate jdbcTemplate; + + @Override + public void addUser(User user) { + jdbcTemplate.update("INSERT INTO user (name,age) VALUES(?, ?)", new Object[]{user.getName(), user.getAge()}); + } + + @DS("slave_1") + @Override + public List selectUsersFromDs() { + return jdbcTemplate.queryForList("SELECT * FROM user"); + } + + @DS("slave") + @Override + public List selectUserFromDsGroup() { + return jdbcTemplate.queryForList("SELECT * FROM user"); + } +} diff --git a/samples/dynamic-jdbc-template-sample/src/main/resources/application.yml b/samples/dynamic-jdbc-template-sample/src/main/resources/application.yml new file mode 100644 index 0000000..60398eb --- /dev/null +++ b/samples/dynamic-jdbc-template-sample/src/main/resources/application.yml @@ -0,0 +1,27 @@ +spring: + datasource: + dynamic: + datasource: + master: + username: sa + password: "" + url: jdbc:h2:mem:test + driver-class-name: org.h2.Driver + slave_1: + username: sa + password: "" + url: jdbc:h2:mem:test + driver-class-name: org.h2.Driver + slave_2: + username: sa + password: "" + url: jdbc:h2:mem:test + driver-class-name: org.h2.Driver + slave_3: + username: sa + password: "" + url: jdbc:h2:mem:test + driver-class-name: org.h2.Driver +logging: + level: + com.baomidou: debug \ No newline at end of file diff --git a/samples/dynamic-jdbc-template-sample/src/test/java/com/baomidou/samples/jdbc/test/ApplicationTest.java b/samples/dynamic-jdbc-template-sample/src/test/java/com/baomidou/samples/jdbc/test/ApplicationTest.java new file mode 100644 index 0000000..c6ec9d6 --- /dev/null +++ b/samples/dynamic-jdbc-template-sample/src/test/java/com/baomidou/samples/jdbc/test/ApplicationTest.java @@ -0,0 +1,64 @@ +package com.baomidou.samples.jdbc.test; + +import com.baomidou.samples.jdbc.Application; +import com.baomidou.samples.jdbc.entity.User; +import com.baomidou.samples.jdbc.service.UserService; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Random; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = Application.class) +public class ApplicationTest { + + private Random random = new Random(); + + @Autowired + private UserService userService; + + @Autowired + private DataSource dataSource; + + @Before + public void beforeTest() { + try { + Connection connection = dataSource.getConnection(); + connection.createStatement().execute("CREATE TABLE IF NOT EXISTS USER (\n" + + " id BIGINT(20) NOT NULL AUTO_INCREMENT,\n" + + " name VARCHAR(30) NULL DEFAULT NULL ,\n" + + " age INT(11) NULL DEFAULT NULL ,\n" + + " PRIMARY KEY (id)\n" + + ");"); + connection.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + + @Test + public void addUser() { + User user = new User(); + user.setName("测试用户" + random.nextInt()); + user.setAge(random.nextInt(100)); + userService.addUser(user); + } + + @Test + public void selectUsersFromDs() { + userService.selectUsersFromDs(); + } + + @Test + public void selectUserFromDsGroup() { + userService.selectUserFromDsGroup(); + } +} diff --git a/samples/dynamic-mybatis-sample/pom.xml b/samples/dynamic-mybatis-sample/pom.xml new file mode 100644 index 0000000..58d1252 --- /dev/null +++ b/samples/dynamic-mybatis-sample/pom.xml @@ -0,0 +1,22 @@ + + + + dynamic-datsource-samples + com.baomidou + 1.0.0 + + 4.0.0 + + dynamic-mybatis-sample + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 1.3.2 + + + + \ No newline at end of file diff --git a/samples/dynamic-mybatis-sample/src/main/java/com/baomidou/samples/mybatis/Application.java b/samples/dynamic-mybatis-sample/src/main/java/com/baomidou/samples/mybatis/Application.java new file mode 100644 index 0000000..77931a1 --- /dev/null +++ b/samples/dynamic-mybatis-sample/src/main/java/com/baomidou/samples/mybatis/Application.java @@ -0,0 +1,15 @@ +package com.baomidou.samples.mybatis; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +@MapperScan("com.baomidou.samples.mybatis.mapper") +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} \ No newline at end of file diff --git a/samples/dynamic-mybatis-sample/src/main/java/com/baomidou/samples/mybatis/entity/User.java b/samples/dynamic-mybatis-sample/src/main/java/com/baomidou/samples/mybatis/entity/User.java new file mode 100644 index 0000000..a0cd06e --- /dev/null +++ b/samples/dynamic-mybatis-sample/src/main/java/com/baomidou/samples/mybatis/entity/User.java @@ -0,0 +1,34 @@ +package com.baomidou.samples.mybatis.entity; + +public class User { + + private Integer id; + + private String name; + + private Integer age; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } +} diff --git a/samples/dynamic-mybatis-sample/src/main/java/com/baomidou/samples/mybatis/mapper/UserMapper.java b/samples/dynamic-mybatis-sample/src/main/java/com/baomidou/samples/mybatis/mapper/UserMapper.java new file mode 100644 index 0000000..8940fa4 --- /dev/null +++ b/samples/dynamic-mybatis-sample/src/main/java/com/baomidou/samples/mybatis/mapper/UserMapper.java @@ -0,0 +1,18 @@ +package com.baomidou.samples.mybatis.mapper; + +import com.baomidou.samples.mybatis.entity.User; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +//@DS("slave")这里可以使用但不建议,不要和service同时使用 +public interface UserMapper { + + @Insert("INSERT INTO user (name,age) values (#{name},#{age})") + boolean addUser(@Param("name") String name, @Param("age") Integer age); + + @Select("SELECT * FROM user") + List selectUsers(); +} diff --git a/samples/dynamic-mybatis-sample/src/main/java/com/baomidou/samples/mybatis/service/UserService.java b/samples/dynamic-mybatis-sample/src/main/java/com/baomidou/samples/mybatis/service/UserService.java new file mode 100644 index 0000000..b2fdc3d --- /dev/null +++ b/samples/dynamic-mybatis-sample/src/main/java/com/baomidou/samples/mybatis/service/UserService.java @@ -0,0 +1,15 @@ +package com.baomidou.samples.mybatis.service; + + +import com.baomidou.samples.mybatis.entity.User; + +import java.util.List; + +public interface UserService { + + void addUser(User user); + + List selectUsersFromDs(); + + List selectUserFromDsGroup(); +} diff --git a/samples/dynamic-mybatis-sample/src/main/java/com/baomidou/samples/mybatis/service/impl/UserServiceImpl.java b/samples/dynamic-mybatis-sample/src/main/java/com/baomidou/samples/mybatis/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..3b1cf08 --- /dev/null +++ b/samples/dynamic-mybatis-sample/src/main/java/com/baomidou/samples/mybatis/service/impl/UserServiceImpl.java @@ -0,0 +1,35 @@ +package com.baomidou.samples.mybatis.service.impl; + + +import com.baomidou.dynamic.datasource.annotation.DS; +import com.baomidou.samples.mybatis.entity.User; +import com.baomidou.samples.mybatis.mapper.UserMapper; +import com.baomidou.samples.mybatis.service.UserService; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.List; + +@Service +public class UserServiceImpl implements UserService { + + @Resource + private UserMapper userMapper; + + @Override + public void addUser(User user) { + userMapper.addUser(user.getName(), user.getAge()); + } + + @DS("slave_1") + @Override + public List selectUsersFromDs() { + return userMapper.selectUsers(); + } + + @DS("slave") + @Override + public List selectUserFromDsGroup() { + return userMapper.selectUsers(); + } +} diff --git a/samples/dynamic-mybatis-sample/src/main/resources/application.yml b/samples/dynamic-mybatis-sample/src/main/resources/application.yml new file mode 100644 index 0000000..60398eb --- /dev/null +++ b/samples/dynamic-mybatis-sample/src/main/resources/application.yml @@ -0,0 +1,27 @@ +spring: + datasource: + dynamic: + datasource: + master: + username: sa + password: "" + url: jdbc:h2:mem:test + driver-class-name: org.h2.Driver + slave_1: + username: sa + password: "" + url: jdbc:h2:mem:test + driver-class-name: org.h2.Driver + slave_2: + username: sa + password: "" + url: jdbc:h2:mem:test + driver-class-name: org.h2.Driver + slave_3: + username: sa + password: "" + url: jdbc:h2:mem:test + driver-class-name: org.h2.Driver +logging: + level: + com.baomidou: debug \ No newline at end of file diff --git a/samples/dynamic-mybatis-sample/src/test/java/com/baomidou/samples/mybatis/test/ApplicationTest.java b/samples/dynamic-mybatis-sample/src/test/java/com/baomidou/samples/mybatis/test/ApplicationTest.java new file mode 100644 index 0000000..82a563b --- /dev/null +++ b/samples/dynamic-mybatis-sample/src/test/java/com/baomidou/samples/mybatis/test/ApplicationTest.java @@ -0,0 +1,65 @@ +package com.baomidou.samples.mybatis.test; + + +import com.baomidou.samples.mybatis.Application; +import com.baomidou.samples.mybatis.entity.User; +import com.baomidou.samples.mybatis.service.UserService; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Random; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = Application.class) +public class ApplicationTest { + + private Random random = new Random(); + + @Autowired + private UserService userService; + + @Autowired + private DataSource dataSource; + + @Before + public void beforeTest() { + try { + Connection connection = dataSource.getConnection(); + connection.createStatement().execute("CREATE TABLE IF NOT EXISTS USER (\n" + + " id BIGINT(20) NOT NULL AUTO_INCREMENT,\n" + + " name VARCHAR(30) NULL DEFAULT NULL ,\n" + + " age INT(11) NULL DEFAULT NULL ,\n" + + " PRIMARY KEY (id)\n" + + ");"); + connection.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + + @Test + public void addUser() { + User user = new User(); + user.setName("测试用户" + random.nextInt()); + user.setAge(random.nextInt(100)); + userService.addUser(user); + } + + @Test + public void selectUsersFromDs() { + userService.selectUsersFromDs(); + } + + @Test + public void selectUserFromDsGroup() { + userService.selectUserFromDsGroup(); + } +} diff --git a/samples/dynamic-mybatis-sample/transaction-logs/tmlog49.log b/samples/dynamic-mybatis-sample/transaction-logs/tmlog49.log new file mode 100644 index 0000000..e69de29 diff --git a/samples/dynamic-mybatisplus2-sample/pom.xml b/samples/dynamic-mybatisplus2-sample/pom.xml new file mode 100644 index 0000000..30638c1 --- /dev/null +++ b/samples/dynamic-mybatisplus2-sample/pom.xml @@ -0,0 +1,22 @@ + + + + dynamic-datsource-samples + com.baomidou + 1.0.0 + + 4.0.0 + + dynamic-mybatisplus2-sample + + + + com.baomidou + mybatis-plus-boot-starter + 2.3.1 + + + + \ No newline at end of file diff --git a/samples/dynamic-mybatisplus2-sample/src/main/java/com/baomidou/samples/mybatisplus2/Application.java b/samples/dynamic-mybatisplus2-sample/src/main/java/com/baomidou/samples/mybatisplus2/Application.java new file mode 100644 index 0000000..f83177c --- /dev/null +++ b/samples/dynamic-mybatisplus2-sample/src/main/java/com/baomidou/samples/mybatisplus2/Application.java @@ -0,0 +1,15 @@ +package com.baomidou.samples.mybatisplus2; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +@MapperScan("com.baomidou.samples.mybatisplus2.mapper") +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} \ No newline at end of file diff --git a/samples/dynamic-mybatisplus2-sample/src/main/java/com/baomidou/samples/mybatisplus2/entity/User.java b/samples/dynamic-mybatisplus2-sample/src/main/java/com/baomidou/samples/mybatisplus2/entity/User.java new file mode 100644 index 0000000..fea7ad5 --- /dev/null +++ b/samples/dynamic-mybatisplus2-sample/src/main/java/com/baomidou/samples/mybatisplus2/entity/User.java @@ -0,0 +1,38 @@ +package com.baomidou.samples.mybatisplus2.entity; + +import com.baomidou.mybatisplus.annotations.TableId; +import com.baomidou.mybatisplus.enums.IdType; + +public class User { + + @TableId(type = IdType.AUTO) + private Integer id; + + private String name; + + private Integer age; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } +} diff --git a/samples/dynamic-mybatisplus2-sample/src/main/java/com/baomidou/samples/mybatisplus2/mapper/UserMapper.java b/samples/dynamic-mybatisplus2-sample/src/main/java/com/baomidou/samples/mybatisplus2/mapper/UserMapper.java new file mode 100644 index 0000000..142d075 --- /dev/null +++ b/samples/dynamic-mybatisplus2-sample/src/main/java/com/baomidou/samples/mybatisplus2/mapper/UserMapper.java @@ -0,0 +1,7 @@ +package com.baomidou.samples.mybatisplus2.mapper; + +import com.baomidou.mybatisplus.mapper.BaseMapper; +import com.baomidou.samples.mybatisplus2.entity.User; + +public interface UserMapper extends BaseMapper { +} diff --git a/samples/dynamic-mybatisplus2-sample/src/main/java/com/baomidou/samples/mybatisplus2/service/UserService.java b/samples/dynamic-mybatisplus2-sample/src/main/java/com/baomidou/samples/mybatisplus2/service/UserService.java new file mode 100644 index 0000000..e7b4c16 --- /dev/null +++ b/samples/dynamic-mybatisplus2-sample/src/main/java/com/baomidou/samples/mybatisplus2/service/UserService.java @@ -0,0 +1,11 @@ +package com.baomidou.samples.mybatisplus2.service; + + +import com.baomidou.mybatisplus.service.IService; +import com.baomidou.samples.mybatisplus2.entity.User; + +public interface UserService extends IService { + + void addUser(User user); + +} diff --git a/samples/dynamic-mybatisplus2-sample/src/main/java/com/baomidou/samples/mybatisplus2/service/impl/UserServiceImpl.java b/samples/dynamic-mybatisplus2-sample/src/main/java/com/baomidou/samples/mybatisplus2/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..38c3d9d --- /dev/null +++ b/samples/dynamic-mybatisplus2-sample/src/main/java/com/baomidou/samples/mybatisplus2/service/impl/UserServiceImpl.java @@ -0,0 +1,20 @@ +package com.baomidou.samples.mybatisplus2.service.impl; + + +import com.baomidou.dynamic.datasource.annotation.DS; +import com.baomidou.mybatisplus.service.impl.ServiceImpl; +import com.baomidou.samples.mybatisplus2.entity.User; +import com.baomidou.samples.mybatisplus2.mapper.UserMapper; +import com.baomidou.samples.mybatisplus2.service.UserService; +import org.springframework.stereotype.Service; + +@Service +@DS("slave") +public class UserServiceImpl extends ServiceImpl implements UserService { + + @Override + @DS("master")//这里必须包一层,不能调用mp默认的插入,因为会走到从库去 + public void addUser(User user) { + baseMapper.insert(user); + } +} diff --git a/samples/dynamic-mybatisplus2-sample/src/main/resources/application.yml b/samples/dynamic-mybatisplus2-sample/src/main/resources/application.yml new file mode 100644 index 0000000..60398eb --- /dev/null +++ b/samples/dynamic-mybatisplus2-sample/src/main/resources/application.yml @@ -0,0 +1,27 @@ +spring: + datasource: + dynamic: + datasource: + master: + username: sa + password: "" + url: jdbc:h2:mem:test + driver-class-name: org.h2.Driver + slave_1: + username: sa + password: "" + url: jdbc:h2:mem:test + driver-class-name: org.h2.Driver + slave_2: + username: sa + password: "" + url: jdbc:h2:mem:test + driver-class-name: org.h2.Driver + slave_3: + username: sa + password: "" + url: jdbc:h2:mem:test + driver-class-name: org.h2.Driver +logging: + level: + com.baomidou: debug \ No newline at end of file diff --git a/samples/dynamic-mybatisplus2-sample/src/test/java/com/baomidou/samples/mybatisplus2/test/ApplicationTest.java b/samples/dynamic-mybatisplus2-sample/src/test/java/com/baomidou/samples/mybatisplus2/test/ApplicationTest.java new file mode 100644 index 0000000..09788de --- /dev/null +++ b/samples/dynamic-mybatisplus2-sample/src/test/java/com/baomidou/samples/mybatisplus2/test/ApplicationTest.java @@ -0,0 +1,58 @@ +package com.baomidou.samples.mybatisplus2.test; + +import com.baomidou.samples.mybatisplus2.Application; +import com.baomidou.samples.mybatisplus2.entity.User; +import com.baomidou.samples.mybatisplus2.service.UserService; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Random; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = Application.class) +public class ApplicationTest { + + private Random random = new Random(); + + @Autowired + private DataSource dataSource; + @Autowired + private UserService userService; + + @Before + public void beforeTest() { + try { + Connection connection = dataSource.getConnection(); + connection.createStatement().execute("CREATE TABLE IF NOT EXISTS USER (\n" + + " id BIGINT(20) NOT NULL AUTO_INCREMENT,\n" + + " name VARCHAR(30) NULL DEFAULT NULL ,\n" + + " age INT(11) NULL DEFAULT NULL ,\n" + + " PRIMARY KEY (id)\n" + + ");"); + connection.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + @Test + public void testAddUser() { + User user = new User(); + user.setName("测试用户" + random.nextInt()); + user.setAge(random.nextInt(100)); + userService.addUser(user); + } + + @Test + public void testSelectUser() { + userService.selectList(null); + } + +} diff --git a/samples/dynamic-mybatisplus3-sample/pom.xml b/samples/dynamic-mybatisplus3-sample/pom.xml new file mode 100644 index 0000000..6384f29 --- /dev/null +++ b/samples/dynamic-mybatisplus3-sample/pom.xml @@ -0,0 +1,22 @@ + + + + dynamic-datsource-samples + com.baomidou + 1.0.0 + + 4.0.0 + + dynamic-mybatisplus3-sample + + + + com.baomidou + mybatis-plus-boot-starter + 3.0.3 + + + + \ No newline at end of file diff --git a/samples/dynamic-mybatisplus3-sample/src/main/java/com/baomidou/samples/mybatisplus/Application.java b/samples/dynamic-mybatisplus3-sample/src/main/java/com/baomidou/samples/mybatisplus/Application.java new file mode 100644 index 0000000..1df5795 --- /dev/null +++ b/samples/dynamic-mybatisplus3-sample/src/main/java/com/baomidou/samples/mybatisplus/Application.java @@ -0,0 +1,15 @@ +package com.baomidou.samples.mybatisplus; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +@MapperScan("com.baomidou.samples.mybatisplus.mapper") +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} \ No newline at end of file diff --git a/samples/dynamic-mybatisplus3-sample/src/main/java/com/baomidou/samples/mybatisplus/entity/User.java b/samples/dynamic-mybatisplus3-sample/src/main/java/com/baomidou/samples/mybatisplus/entity/User.java new file mode 100644 index 0000000..eefc4a1 --- /dev/null +++ b/samples/dynamic-mybatisplus3-sample/src/main/java/com/baomidou/samples/mybatisplus/entity/User.java @@ -0,0 +1,38 @@ +package com.baomidou.samples.mybatisplus.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; + +public class User { + + @TableId(type = IdType.AUTO) + private Integer id; + + private String name; + + private Integer age; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } +} diff --git a/samples/dynamic-mybatisplus3-sample/src/main/java/com/baomidou/samples/mybatisplus/mapper/UserMapper.java b/samples/dynamic-mybatisplus3-sample/src/main/java/com/baomidou/samples/mybatisplus/mapper/UserMapper.java new file mode 100644 index 0000000..faf2496 --- /dev/null +++ b/samples/dynamic-mybatisplus3-sample/src/main/java/com/baomidou/samples/mybatisplus/mapper/UserMapper.java @@ -0,0 +1,7 @@ +package com.baomidou.samples.mybatisplus.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.samples.mybatisplus.entity.User; + +public interface UserMapper extends BaseMapper { +} diff --git a/samples/dynamic-mybatisplus3-sample/src/main/java/com/baomidou/samples/mybatisplus/service/UserService.java b/samples/dynamic-mybatisplus3-sample/src/main/java/com/baomidou/samples/mybatisplus/service/UserService.java new file mode 100644 index 0000000..5f3b9f2 --- /dev/null +++ b/samples/dynamic-mybatisplus3-sample/src/main/java/com/baomidou/samples/mybatisplus/service/UserService.java @@ -0,0 +1,11 @@ +package com.baomidou.samples.mybatisplus.service; + + +import com.baomidou.mybatisplus.extension.service.IService; +import com.baomidou.samples.mybatisplus.entity.User; + +public interface UserService extends IService { + + void addUser(User user); + +} diff --git a/samples/dynamic-mybatisplus3-sample/src/main/java/com/baomidou/samples/mybatisplus/service/impl/UserServiceImpl.java b/samples/dynamic-mybatisplus3-sample/src/main/java/com/baomidou/samples/mybatisplus/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..0d53ae1 --- /dev/null +++ b/samples/dynamic-mybatisplus3-sample/src/main/java/com/baomidou/samples/mybatisplus/service/impl/UserServiceImpl.java @@ -0,0 +1,20 @@ +package com.baomidou.samples.mybatisplus.service.impl; + + +import com.baomidou.dynamic.datasource.annotation.DS; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.baomidou.samples.mybatisplus.entity.User; +import com.baomidou.samples.mybatisplus.mapper.UserMapper; +import com.baomidou.samples.mybatisplus.service.UserService; +import org.springframework.stereotype.Service; + +@Service +@DS("slave") +public class UserServiceImpl extends ServiceImpl implements UserService { + + @Override + @DS("master")//这里必须包一层,不能调用mp默认的插入,因为会走到从库去 + public void addUser(User user) { + baseMapper.insert(user); + } +} diff --git a/samples/dynamic-mybatisplus3-sample/src/main/resources/application.yml b/samples/dynamic-mybatisplus3-sample/src/main/resources/application.yml new file mode 100644 index 0000000..60398eb --- /dev/null +++ b/samples/dynamic-mybatisplus3-sample/src/main/resources/application.yml @@ -0,0 +1,27 @@ +spring: + datasource: + dynamic: + datasource: + master: + username: sa + password: "" + url: jdbc:h2:mem:test + driver-class-name: org.h2.Driver + slave_1: + username: sa + password: "" + url: jdbc:h2:mem:test + driver-class-name: org.h2.Driver + slave_2: + username: sa + password: "" + url: jdbc:h2:mem:test + driver-class-name: org.h2.Driver + slave_3: + username: sa + password: "" + url: jdbc:h2:mem:test + driver-class-name: org.h2.Driver +logging: + level: + com.baomidou: debug \ No newline at end of file diff --git a/samples/dynamic-mybatisplus3-sample/src/test/java/com/baomidou/samples/mybatisplus3/test/ApplicationTest.java b/samples/dynamic-mybatisplus3-sample/src/test/java/com/baomidou/samples/mybatisplus3/test/ApplicationTest.java new file mode 100644 index 0000000..fa91923 --- /dev/null +++ b/samples/dynamic-mybatisplus3-sample/src/test/java/com/baomidou/samples/mybatisplus3/test/ApplicationTest.java @@ -0,0 +1,57 @@ +package com.baomidou.samples.mybatisplus3.test; + +import com.baomidou.samples.mybatisplus.Application; +import com.baomidou.samples.mybatisplus.entity.User; +import com.baomidou.samples.mybatisplus.service.UserService; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Random; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = Application.class) +public class ApplicationTest { + + private Random random = new Random(); + + @Autowired + private DataSource dataSource; + @Autowired + private UserService userService; + + @Before + public void beforeTest() { + try { + Connection connection = dataSource.getConnection(); + connection.createStatement().execute("CREATE TABLE IF NOT EXISTS USER (\n" + + " id BIGINT(20) NOT NULL AUTO_INCREMENT,\n" + + " name VARCHAR(30) NULL DEFAULT NULL ,\n" + + " age INT(11) NULL DEFAULT NULL ,\n" + + " PRIMARY KEY (id)\n" + + ");"); + connection.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + @Test + public void testAddUser() { + User user = new User(); + user.setName("测试用户" + random.nextInt()); + user.setAge(random.nextInt(100)); + userService.addUser(user); + } + + @Test + public void testSelectUser() { + userService.list(null); + } +} diff --git a/samples/dynamic-nest-sample/pom.xml b/samples/dynamic-nest-sample/pom.xml new file mode 100644 index 0000000..a925270 --- /dev/null +++ b/samples/dynamic-nest-sample/pom.xml @@ -0,0 +1,21 @@ + + + + dynamic-datsource-samples + com.baomidou + 1.0.0 + + 4.0.0 + + dynamic-nest-sample + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 1.3.2 + + + \ No newline at end of file diff --git a/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/Application.java b/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/Application.java new file mode 100644 index 0000000..416c0a5 --- /dev/null +++ b/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/Application.java @@ -0,0 +1,15 @@ +package com.baomidou.samples.nest; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +@MapperScan("com.baomidou.samples.nest.mapper") +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} \ No newline at end of file diff --git a/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/CreateDatabaseOnStarted.java b/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/CreateDatabaseOnStarted.java new file mode 100644 index 0000000..39fdc4e --- /dev/null +++ b/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/CreateDatabaseOnStarted.java @@ -0,0 +1,39 @@ +package com.baomidou.samples.nest; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.event.ApplicationStartedEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; + +@Component +public class CreateDatabaseOnStarted implements ApplicationListener { + + @Autowired + private DataSource dataSource; + + @Override + public void onApplicationEvent(ApplicationStartedEvent event) { + try { + Connection connection = dataSource.getConnection(); + connection.createStatement().execute("CREATE TABLE IF NOT EXISTS teacher (\n" + + " id BIGINT(20) NOT NULL AUTO_INCREMENT,\n" + + " name VARCHAR(30) NULL DEFAULT NULL ,\n" + + " age INT(11) NULL DEFAULT NULL ,\n" + + " PRIMARY KEY (id)\n" + + ");"); + connection.createStatement().execute("CREATE TABLE IF NOT EXISTS student (\n" + + " id BIGINT(20) NOT NULL AUTO_INCREMENT,\n" + + " name VARCHAR(30) NULL DEFAULT NULL ,\n" + + " age INT(11) NULL DEFAULT NULL ,\n" + + " PRIMARY KEY (id)\n" + + ");"); + connection.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } +} diff --git a/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/controller/TestController.java b/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/controller/TestController.java new file mode 100644 index 0000000..8b69a5a --- /dev/null +++ b/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/controller/TestController.java @@ -0,0 +1,34 @@ +package com.baomidou.samples.nest.controller; + +import com.baomidou.samples.nest.service.SchoolService; +import com.baomidou.samples.nest.service.TeacherService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class TestController { + @Autowired + private TeacherService teacherService; + + @Autowired + private SchoolService schoolService; + + @GetMapping("/tx1") + public void tx1() { + //外层不加事物,里面每个单独加事物(支持,不过感觉没毛用) + schoolService.addTeacherAndStudent(); + } + + @GetMapping("/tx2") + public void tx2() { + //外层加事物,里面每个也加事物(不支持!!)其实只要加了事物就不支持多库 + schoolService.addTeacherAndStudentWithTx(); + } + + @GetMapping("/tx3") + public void tx3() { + //单独调用加事物单库(支持) + teacherService.addTeacherWithTx("tt", 12); + } +} diff --git a/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/entiry/Student.java b/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/entiry/Student.java new file mode 100644 index 0000000..b788dd8 --- /dev/null +++ b/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/entiry/Student.java @@ -0,0 +1,34 @@ +package com.baomidou.samples.nest.entiry; + +public class Student { + + private Integer id; + + private String name; + + private Integer age; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } +} diff --git a/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/entiry/Teacher.java b/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/entiry/Teacher.java new file mode 100644 index 0000000..bbd30d5 --- /dev/null +++ b/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/entiry/Teacher.java @@ -0,0 +1,34 @@ +package com.baomidou.samples.nest.entiry; + +public class Teacher { + + private Integer id; + + private String name; + + private Integer age; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } +} diff --git a/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/mapper/StudentMapper.java b/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/mapper/StudentMapper.java new file mode 100644 index 0000000..c283e57 --- /dev/null +++ b/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/mapper/StudentMapper.java @@ -0,0 +1,17 @@ +package com.baomidou.samples.nest.mapper; + +import com.baomidou.samples.nest.entiry.Student; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +public interface StudentMapper { + + @Insert("INSERT INTO student (name,age) values (#{name},#{age})") + boolean addStudent(@Param("name") String name, @Param("age") Integer age); + + @Select("SELECT * FROM student") + List selectStudents(); +} diff --git a/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/mapper/TeacherMapper.java b/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/mapper/TeacherMapper.java new file mode 100644 index 0000000..395e025 --- /dev/null +++ b/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/mapper/TeacherMapper.java @@ -0,0 +1,17 @@ +package com.baomidou.samples.nest.mapper; + +import com.baomidou.samples.nest.entiry.Teacher; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +public interface TeacherMapper { + + @Insert("INSERT INTO teacher (name,age) values (#{name},#{age})") + boolean addTeacher(@Param("name") String name, @Param("age") Integer age); + + @Select("SELECT * FROM teacher") + List selectTeachers(); +} diff --git a/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/service/SchoolService.java b/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/service/SchoolService.java new file mode 100644 index 0000000..4234b7b --- /dev/null +++ b/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/service/SchoolService.java @@ -0,0 +1,12 @@ +package com.baomidou.samples.nest.service; + +public interface SchoolService { + + void selectTeachersAndStudents(); + + void selectTeachersInnerStudents(); + + void addTeacherAndStudent(); + + void addTeacherAndStudentWithTx(); +} diff --git a/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/service/StudentService.java b/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/service/StudentService.java new file mode 100644 index 0000000..970f49a --- /dev/null +++ b/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/service/StudentService.java @@ -0,0 +1,15 @@ +package com.baomidou.samples.nest.service; + +import com.baomidou.samples.nest.entiry.Student; + +import java.util.List; + +public interface StudentService { + + boolean addStudentWithTx(String name, Integer age); + + boolean addStudentNoTx(String name, Integer age); + + + List selectStudents(); +} diff --git a/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/service/TeacherService.java b/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/service/TeacherService.java new file mode 100644 index 0000000..9b92991 --- /dev/null +++ b/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/service/TeacherService.java @@ -0,0 +1,18 @@ +package com.baomidou.samples.nest.service; + +import com.baomidou.samples.nest.entiry.Teacher; + +import java.util.List; + + +public interface TeacherService { + + boolean addTeacherWithTx(String name, Integer age); + + boolean addTeacherNoTx(String name, Integer age); + + + List selectTeachers(); + + void selectTeachersInnerStudents(); +} diff --git a/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/service/impl/SchoolServiceImpl.java b/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/service/impl/SchoolServiceImpl.java new file mode 100644 index 0000000..1f3b133 --- /dev/null +++ b/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/service/impl/SchoolServiceImpl.java @@ -0,0 +1,42 @@ +package com.baomidou.samples.nest.service.impl; + +import com.baomidou.samples.nest.service.SchoolService; +import com.baomidou.samples.nest.service.StudentService; +import com.baomidou.samples.nest.service.TeacherService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class SchoolServiceImpl implements SchoolService { + + @Autowired + private TeacherService teacherService; + + @Autowired + private StudentService studentService; + + @Override + public void selectTeachersAndStudents() { + teacherService.selectTeachers(); + studentService.selectStudents(); + } + + @Override + public void selectTeachersInnerStudents() { + teacherService.selectTeachersInnerStudents(); + } + + @Override + public void addTeacherAndStudent() { + teacherService.addTeacherWithTx("ss", 1); + studentService.addStudentWithTx("tt", 2); + } + + @Override + @Transactional + public void addTeacherAndStudentWithTx() { + teacherService.addTeacherWithTx("ss", 1); + studentService.addStudentWithTx("tt", 2); + } +} diff --git a/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/service/impl/StudentServiceImpl.java b/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/service/impl/StudentServiceImpl.java new file mode 100644 index 0000000..63b3882 --- /dev/null +++ b/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/service/impl/StudentServiceImpl.java @@ -0,0 +1,35 @@ +package com.baomidou.samples.nest.service.impl; + +import com.baomidou.dynamic.datasource.annotation.DS; +import com.baomidou.samples.nest.entiry.Student; +import com.baomidou.samples.nest.mapper.StudentMapper; +import com.baomidou.samples.nest.service.StudentService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.util.List; + +@Service +@DS("student") +public class StudentServiceImpl implements StudentService { + + @Resource + private StudentMapper studentMapper; + + @Override + @Transactional + public boolean addStudentWithTx(String name, Integer age) { + return studentMapper.addStudent(name, age); + } + + @Override + public boolean addStudentNoTx(String name, Integer age) { + return studentMapper.addStudent(name, age); + } + + @Override + public List selectStudents() { + return studentMapper.selectStudents(); + } +} diff --git a/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/service/impl/TeacherServiceImpl.java b/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/service/impl/TeacherServiceImpl.java new file mode 100644 index 0000000..9d7550b --- /dev/null +++ b/samples/dynamic-nest-sample/src/main/java/com/baomidou/samples/nest/service/impl/TeacherServiceImpl.java @@ -0,0 +1,45 @@ +package com.baomidou.samples.nest.service.impl; + +import com.baomidou.dynamic.datasource.annotation.DS; +import com.baomidou.samples.nest.entiry.Teacher; +import com.baomidou.samples.nest.mapper.TeacherMapper; +import com.baomidou.samples.nest.service.StudentService; +import com.baomidou.samples.nest.service.TeacherService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.util.List; + +@Service +@DS("teacher") +public class TeacherServiceImpl implements TeacherService { + + @Resource + private TeacherMapper teacherMapper; + @Autowired + private StudentService studentService; + + @Override + @Transactional + public boolean addTeacherWithTx(String name, Integer age) { + return teacherMapper.addTeacher(name, age); + } + + @Override + public boolean addTeacherNoTx(String name, Integer age) { + return teacherMapper.addTeacher(name, age); + } + + @Override + public List selectTeachers() { + return teacherMapper.selectTeachers(); + } + + @Override + public void selectTeachersInnerStudents() { + teacherMapper.selectTeachers(); + studentService.selectStudents(); + } +} diff --git a/samples/dynamic-nest-sample/src/main/resources/application.yml b/samples/dynamic-nest-sample/src/main/resources/application.yml new file mode 100644 index 0000000..e735650 --- /dev/null +++ b/samples/dynamic-nest-sample/src/main/resources/application.yml @@ -0,0 +1,27 @@ +spring: + datasource: + dynamic: + datasource: + master: + username: sa + password: "" + url: jdbc:h2:mem:test;DB_CLOSE_ON_EXIT=FALSE + driver-class-name: org.h2.Driver + salve: + username: sa + password: "" + url: jdbc:h2:mem:test;DB_CLOSE_ON_EXIT=FALSE + driver-class-name: org.h2.Driver + teacher: + username: sa + password: "" + url: jdbc:h2:mem:test;DB_CLOSE_ON_EXIT=FALSE + driver-class-name: org.h2.Driver + student: + username: sa + password: "" + url: jdbc:h2:mem:test;DB_CLOSE_ON_EXIT=FALSE + driver-class-name: org.h2.Driver +logging: + level: + com.baomidou: debug \ No newline at end of file diff --git a/samples/dynamic-nest-sample/src/test/java/com/baomidou/samples/nest/test/ApplicationTest.java b/samples/dynamic-nest-sample/src/test/java/com/baomidou/samples/nest/test/ApplicationTest.java new file mode 100644 index 0000000..91430af --- /dev/null +++ b/samples/dynamic-nest-sample/src/test/java/com/baomidou/samples/nest/test/ApplicationTest.java @@ -0,0 +1,81 @@ +package com.baomidou.samples.nest.test; + + +import com.baomidou.samples.nest.Application; +import com.baomidou.samples.nest.service.SchoolService; +import com.baomidou.samples.nest.service.StudentService; +import com.baomidou.samples.nest.service.TeacherService; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Random; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = Application.class) + +public class ApplicationTest { + + private Random random = new Random(); + + @Autowired + private TeacherService teacherService; + @Autowired + private StudentService studentService; + @Autowired + private SchoolService schoolService; + + @Autowired + private DataSource dataSource; + + @Before + public void beforeTest() { + try { + Connection connection = dataSource.getConnection(); + connection.createStatement().execute("CREATE TABLE IF NOT EXISTS teacher (\n" + + " id BIGINT(20) NOT NULL AUTO_INCREMENT,\n" + + " name VARCHAR(30) NULL DEFAULT NULL ,\n" + + " age INT(11) NULL DEFAULT NULL ,\n" + + " PRIMARY KEY (id)\n" + + ");"); + connection.createStatement().execute("CREATE TABLE IF NOT EXISTS student (\n" + + " id BIGINT(20) NOT NULL AUTO_INCREMENT,\n" + + " name VARCHAR(30) NULL DEFAULT NULL ,\n" + + " age INT(11) NULL DEFAULT NULL ,\n" + + " PRIMARY KEY (id)\n" + + ");"); + connection.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + + @Test + public void nest1() { + //直接在controller + teacherService.selectTeachers(); + studentService.selectStudents(); + } + + @Test + public void nest2() { + schoolService.selectTeachersAndStudents(); + } + + @Test + public void nest3() { + schoolService.selectTeachersInnerStudents(); + } + + @Test + public void tx() { + teacherService.selectTeachers(); + } +} \ No newline at end of file diff --git a/samples/dynamic-spel-sample/pom.xml b/samples/dynamic-spel-sample/pom.xml new file mode 100644 index 0000000..fae8ccc --- /dev/null +++ b/samples/dynamic-spel-sample/pom.xml @@ -0,0 +1,22 @@ + + + + dynamic-datsource-samples + com.baomidou + 1.0.0 + + 4.0.0 + + dynamic-spel-sample + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 1.3.2 + + + + \ No newline at end of file diff --git a/samples/dynamic-spel-sample/src/main/java/com/baomidou/samples/spel/Application.java b/samples/dynamic-spel-sample/src/main/java/com/baomidou/samples/spel/Application.java new file mode 100644 index 0000000..2f24ff8 --- /dev/null +++ b/samples/dynamic-spel-sample/src/main/java/com/baomidou/samples/spel/Application.java @@ -0,0 +1,15 @@ +package com.baomidou.samples.spel; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +@MapperScan("com.baomidou.samples.spel.mapper") +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} \ No newline at end of file diff --git a/samples/dynamic-spel-sample/src/main/java/com/baomidou/samples/spel/controller/UserController.java b/samples/dynamic-spel-sample/src/main/java/com/baomidou/samples/spel/controller/UserController.java new file mode 100644 index 0000000..8c49eaa --- /dev/null +++ b/samples/dynamic-spel-sample/src/main/java/com/baomidou/samples/spel/controller/UserController.java @@ -0,0 +1,29 @@ +package com.baomidou.samples.spel.controller; + +import com.baomidou.samples.spel.entity.User; +import com.baomidou.samples.spel.service.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; + +@RestController +public class UserController { + @Autowired + private UserService userService; + + @GetMapping("/users") + public List usersFromSession(HttpServletRequest request) { + request.getSession().setAttribute("tenantName", "tenant1"); + return userService.selectSpelBySession(); + } + + @GetMapping("/users/header") + public String usersFromHeader() { + userService.selectSpelByHeader(); + return "success"; + } + +} diff --git a/samples/dynamic-spel-sample/src/main/java/com/baomidou/samples/spel/entity/User.java b/samples/dynamic-spel-sample/src/main/java/com/baomidou/samples/spel/entity/User.java new file mode 100644 index 0000000..3a5f958 --- /dev/null +++ b/samples/dynamic-spel-sample/src/main/java/com/baomidou/samples/spel/entity/User.java @@ -0,0 +1,44 @@ +package com.baomidou.samples.spel.entity; + +public class User { + + private Integer id; + + private String name; + + private Integer age; + + private String tenantName; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } + + public String getTenantName() { + return tenantName; + } + + public void setTenantName(String tenantName) { + this.tenantName = tenantName; + } +} diff --git a/samples/dynamic-spel-sample/src/main/java/com/baomidou/samples/spel/mapper/UserMapper.java b/samples/dynamic-spel-sample/src/main/java/com/baomidou/samples/spel/mapper/UserMapper.java new file mode 100644 index 0000000..ea3df74 --- /dev/null +++ b/samples/dynamic-spel-sample/src/main/java/com/baomidou/samples/spel/mapper/UserMapper.java @@ -0,0 +1,18 @@ +package com.baomidou.samples.spel.mapper; + +import com.baomidou.samples.spel.entity.User; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +//@DS("slave")这里可以使用但不建议,不要和service同时使用 +public interface UserMapper { + + @Insert("INSERT INTO user (name,age) values (#{name},#{age})") + boolean addUser(@Param("name") String name, @Param("age") Integer age); + + @Select("SELECT * FROM user") + List selectUsers(); +} diff --git a/samples/dynamic-spel-sample/src/main/java/com/baomidou/samples/spel/service/UserService.java b/samples/dynamic-spel-sample/src/main/java/com/baomidou/samples/spel/service/UserService.java new file mode 100644 index 0000000..19bb589 --- /dev/null +++ b/samples/dynamic-spel-sample/src/main/java/com/baomidou/samples/spel/service/UserService.java @@ -0,0 +1,17 @@ +package com.baomidou.samples.spel.service; + + +import com.baomidou.samples.spel.entity.User; + +import java.util.List; + +public interface UserService { + + List selectSpelBySession(); + + List selectSpelByHeader(); + + List selectSpelByKey(String tenantName); + + List selecSpelByTenant(User user); +} diff --git a/samples/dynamic-spel-sample/src/main/java/com/baomidou/samples/spel/service/impl/UserServiceImpl.java b/samples/dynamic-spel-sample/src/main/java/com/baomidou/samples/spel/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..53901bb --- /dev/null +++ b/samples/dynamic-spel-sample/src/main/java/com/baomidou/samples/spel/service/impl/UserServiceImpl.java @@ -0,0 +1,43 @@ +package com.baomidou.samples.spel.service.impl; + + +import com.baomidou.dynamic.datasource.annotation.DS; +import com.baomidou.samples.spel.entity.User; +import com.baomidou.samples.spel.mapper.UserMapper; +import com.baomidou.samples.spel.service.UserService; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.List; + +@Service +@DS("slave") +public class UserServiceImpl implements UserService { + + @Resource + private UserMapper userMapper; + + @Override + @DS("#session.tenantName") + public List selectSpelBySession() { + return userMapper.selectUsers(); + } + + @Override + @DS("#header.tenantName") + public List selectSpelByHeader() { + return userMapper.selectUsers(); + } + + @Override + @DS("#tenantName") + public List selectSpelByKey(String tenantName) { + return userMapper.selectUsers(); + } + + @Override + @DS("#user.tenantName") + public List selecSpelByTenant(User user) { + return userMapper.selectUsers(); + } +} diff --git a/samples/dynamic-spel-sample/src/main/resources/application.yml b/samples/dynamic-spel-sample/src/main/resources/application.yml new file mode 100644 index 0000000..bda2072 --- /dev/null +++ b/samples/dynamic-spel-sample/src/main/resources/application.yml @@ -0,0 +1,32 @@ +spring: + datasource: + dynamic: + datasource: + master: + username: sa + password: "" + url: jdbc:h2:mem:test + driver-class-name: org.h2.Driver + tenant1_1: + username: sa + password: "" + url: jdbc:h2:mem:test + driver-class-name: org.h2.Driver + tenant1_2: + username: sa + password: "" + url: jdbc:h2:mem:test + driver-class-name: org.h2.Driver + tenant2_1: + username: sa + password: "" + url: jdbc:h2:mem:test + driver-class-name: org.h2.Driver + tenant2_2: + username: sa + password: "" + url: jdbc:h2:mem:test + driver-class-name: org.h2.Driver +logging: + level: + com.baomidou: debug \ No newline at end of file diff --git a/samples/dynamic-spel-sample/src/test/java/com/baomidou/samples/spel/test/ApplicationTest.java b/samples/dynamic-spel-sample/src/test/java/com/baomidou/samples/spel/test/ApplicationTest.java new file mode 100644 index 0000000..173e225 --- /dev/null +++ b/samples/dynamic-spel-sample/src/test/java/com/baomidou/samples/spel/test/ApplicationTest.java @@ -0,0 +1,92 @@ +package com.baomidou.samples.spel.test; + +import com.baomidou.samples.spel.Application; +import com.baomidou.samples.spel.entity.User; +import com.baomidou.samples.spel.service.UserService; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +import javax.servlet.http.HttpSession; +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; + +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = Application.class) +public class ApplicationTest { + + @Autowired + private WebApplicationContext wac; + + @Autowired + private DataSource dataSource; + + private MockMvc mockMvc; + @Autowired + private UserService userService; + @Autowired + private HttpSession session; + + @Before + public void setup() { + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); + } + + @Before + public void beforeTest() { + try { + Connection connection = dataSource.getConnection(); + connection.createStatement().execute("CREATE TABLE IF NOT EXISTS USER (\n" + + " id BIGINT(20) NOT NULL AUTO_INCREMENT,\n" + + " name VARCHAR(30) NULL DEFAULT NULL ,\n" + + " age INT(11) NULL DEFAULT NULL ,\n" + + " PRIMARY KEY (id)\n" + + ");"); + connection.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + @Test + public void selectSpelBySession() { + session.setAttribute("tenantName", "tenant1"); + userService.selectSpelBySession(); + } + + @Test + public void selectSpelByHeader() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get("/users/header") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("tenantName", "tenant1") + ) + .andDo(print()).andExpect(status().isOk()) + .andReturn().getResponse() + .getContentAsString(); + } + + @Test + public void selectSpelByKey() { + userService.selectSpelByKey("tenant1"); + } + + @Test + public void selecSpelByTenant() { + User user = new User(); + user.setTenantName("tenant2"); + userService.selecSpelByTenant(user); + } + +} diff --git a/samples/pom.xml b/samples/pom.xml new file mode 100644 index 0000000..c45c0b7 --- /dev/null +++ b/samples/pom.xml @@ -0,0 +1,71 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.1.0.RELEASE + + + + com.baomidou + dynamic-datsource-samples + 1.0.0 + pom + + + UTF-8 + UTF-8 + 1.8 + + + + dynamic-jdbc-template-sample + dynamic-mybatis-sample + dynamic-mybatisplus2-sample + dynamic-mybatisplus3-sample + dynamic-druid-mybatis-sample + dynamic-spel-sample + dynamic-nest-sample + + + + + org.springframework.boot + spring-boot-starter-web + + + com.baomidou + dynamic-datasource-spring-boot-starter + 2.3.7 + + + org.springframework.boot + spring-boot-starter-test + test + + + com.h2database + h2 + 1.4.197 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + ${java.version} + ${java.version} + + + + + + \ No newline at end of file diff --git a/src/main/java/com/baomidou/dynamic/datasource/AbstractRoutingDataSource.java b/src/main/java/com/baomidou/dynamic/datasource/AbstractRoutingDataSource.java new file mode 100644 index 0000000..b73e8aa --- /dev/null +++ b/src/main/java/com/baomidou/dynamic/datasource/AbstractRoutingDataSource.java @@ -0,0 +1,63 @@ +/** + * Copyright © 2018 organization baomidou + *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ */
+package com.baomidou.dynamic.datasource;
+
+import org.springframework.jdbc.datasource.AbstractDataSource;
+
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.SQLException;
+
+/**
+ * 抽象动态获取数据源
+ *
+ * @author TaoYu
+ * @since 2.2.0
+ */
+public abstract class AbstractRoutingDataSource extends AbstractDataSource {
+
+    /**
+     * 子类实现决定最终数据源
+     *
+     * @return 数据源
+     */
+    protected abstract DataSource determineDataSource();
+
+    @Override
+    public Connection getConnection() throws SQLException {
+        return determineDataSource().getConnection();
+    }
+
+    @Override
+    public Connection getConnection(String username, String password) throws SQLException {
+        return determineDataSource().getConnection(username, password);
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public  T unwrap(Class iface) throws SQLException {
+        if (iface.isInstance(this)) {
+            return (T) this;
+        }
+        return determineDataSource().unwrap(iface);
+    }
+
+    @Override
+    public boolean isWrapperFor(Class iface) throws SQLException {
+        return (iface.isInstance(this) || determineDataSource().isWrapperFor(iface));
+    }
+}
diff --git a/src/main/java/com/baomidou/dynamic/datasource/DynamicDataSourceClassResolver.java b/src/main/java/com/baomidou/dynamic/datasource/DynamicDataSourceClassResolver.java
new file mode 100644
index 0000000..dcc62e5
--- /dev/null
+++ b/src/main/java/com/baomidou/dynamic/datasource/DynamicDataSourceClassResolver.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright © 2018 organization baomidou
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ */
+package com.baomidou.dynamic.datasource;
+
+import lombok.extern.slf4j.Slf4j;
+import org.aopalliance.intercept.MethodInvocation;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Proxy;
+
+/**
+ * 获取对mybatis-plus的支持
+ *
+ * @author TaoYu
+ * @since 2.3.0
+ */
+@Slf4j
+public class DynamicDataSourceClassResolver {
+
+    private boolean mpEnabled = false;
+
+    private Field mapperInterfaceField;
+
+    public DynamicDataSourceClassResolver() {
+        Class proxyClass = null;
+        try {
+            proxyClass = Class.forName("com.baomidou.mybatisplus.core.override.PageMapperProxy");
+        } catch (ClassNotFoundException e) {
+            try {
+                proxyClass = Class.forName("org.apache.ibatis.binding.MapperProxy");
+            } catch (ClassNotFoundException e1) {
+            }
+        }
+        if (proxyClass != null) {
+            try {
+                mapperInterfaceField = proxyClass.getDeclaredField("mapperInterface");
+                mapperInterfaceField.setAccessible(true);
+                mpEnabled = true;
+            } catch (NoSuchFieldException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    public Class targetClass(MethodInvocation invocation) throws IllegalAccessException {
+        if (mpEnabled) {
+            Object target = invocation.getThis();
+            return Proxy.isProxyClass(target.getClass()) ? (Class) mapperInterfaceField.get(Proxy.getInvocationHandler(target)) : target.getClass();
+        }
+        return invocation.getMethod().getDeclaringClass();
+    }
+}
diff --git a/src/main/java/com/baomidou/dynamic/datasource/DynamicDataSourceCreator.java b/src/main/java/com/baomidou/dynamic/datasource/DynamicDataSourceCreator.java
new file mode 100644
index 0000000..69363d2
--- /dev/null
+++ b/src/main/java/com/baomidou/dynamic/datasource/DynamicDataSourceCreator.java
@@ -0,0 +1,238 @@
+/**
+ * Copyright © 2018 organization baomidou
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ */
+package com.baomidou.dynamic.datasource;
+
+import com.alibaba.druid.pool.DruidDataSource;
+import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
+import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig;
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;
+
+import javax.sql.DataSource;
+import java.lang.reflect.Method;
+import java.sql.SQLException;
+
+/**
+ * 数据源创建器
+ *
+ * @author TaoYu
+ * @since 2.3.0
+ */
+@Slf4j
+public class DynamicDataSourceCreator {
+
+    /**
+     * DRUID数据源类
+     */
+    private static final String DRUID_DATASOURCE = "com.alibaba.druid.pool.DruidDataSource";
+    /**
+     * HikariCp数据源
+     */
+    private static final String HIKARI_DATASOURCE = "com.zaxxer.hikari.HikariDataSource";
+    /**
+     * JNDI数据源查找
+     */
+    private static final JndiDataSourceLookup JNDI_DATA_SOURCE_LOOKUP = new JndiDataSourceLookup();
+
+    private Method createMethod;
+    private Method typeMethod;
+    private Method urlMethod;
+    private Method usernameMethod;
+    private Method passwordMethod;
+    private Method driverClassNameMethod;
+    private Method buildMethod;
+
+    /**
+     * 是否存在druid
+     */
+    private Boolean druidExists = false;
+    /**
+     * 是否存在hikari
+     */
+    private Boolean hikariExists = false;
+
+    @Setter
+    private DruidConfig druidGlobalConfig;
+
+    @Setter
+    private HikariConfig globalHikariConfig;
+
+    public DynamicDataSourceCreator() {
+        Class builderClass = null;
+        try {
+            builderClass = Class.forName("org.springframework.boot.jdbc.DataSourceBuilder");
+        } catch (Exception e) {
+            try {
+                builderClass = Class.forName("org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder");
+            } catch (Exception e1) {
+            }
+        }
+        try {
+            createMethod = builderClass.getDeclaredMethod("create");
+            typeMethod = builderClass.getDeclaredMethod("type", Class.class);
+            urlMethod = builderClass.getDeclaredMethod("url", String.class);
+            usernameMethod = builderClass.getDeclaredMethod("username", String.class);
+            passwordMethod = builderClass.getDeclaredMethod("password", String.class);
+            driverClassNameMethod = builderClass.getDeclaredMethod("driverClassName", String.class);
+            buildMethod = builderClass.getDeclaredMethod("build");
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        try {
+            Class.forName(DRUID_DATASOURCE);
+            log.info("动态数据源-检测到druid存在,如配置中未指定type,druid会默认配置");
+            druidExists = true;
+        } catch (ClassNotFoundException e) {
+        }
+        try {
+            Class.forName(HIKARI_DATASOURCE);
+            hikariExists = true;
+        } catch (ClassNotFoundException e) {
+        }
+    }
+
+    /**
+     * 创建数据源
+     *
+     * @param dataSourceProperty 数据源信息
+     * @return 数据源
+     */
+    public DataSource createDataSource(DataSourceProperty dataSourceProperty) {
+        //如果是jndi数据源
+        String jndiName = dataSourceProperty.getJndiName();
+        if (jndiName != null && !jndiName.isEmpty()) {
+            return createJNDIDataSource(jndiName);
+        }
+        Class type = dataSourceProperty.getType();
+        if (type == null) {
+            if (druidExists) {
+                return createDruidDataSource(dataSourceProperty);
+            } else if (hikariExists) {
+                return createHikariDataSource(dataSourceProperty);
+            }
+        } else if (DRUID_DATASOURCE.equals(type.getName())) {
+            return createDruidDataSource(dataSourceProperty);
+        } else if (HIKARI_DATASOURCE.equals(type.getName())) {
+            return createHikariDataSource(dataSourceProperty);
+        }
+        return createBasicDataSource(dataSourceProperty);
+    }
+
+    /**
+     * 创建基础数据源
+     *
+     * @param dataSourceProperty 数据源参数
+     * @return 数据源
+     */
+    public DataSource createBasicDataSource(DataSourceProperty dataSourceProperty) {
+        try {
+            Object o1 = createMethod.invoke(null);
+            Object o2 = typeMethod.invoke(o1, dataSourceProperty.getType());
+            Object o3 = urlMethod.invoke(o2, dataSourceProperty.getUrl());
+            Object o4 = usernameMethod.invoke(o3, dataSourceProperty.getUsername());
+            Object o5 = passwordMethod.invoke(o4, dataSourceProperty.getPassword());
+            Object o6 = driverClassNameMethod.invoke(o5, dataSourceProperty.getDriverClassName());
+            return (DataSource) buildMethod.invoke(o6);
+        } catch (Exception e) {
+            throw new RuntimeException("多数据源创建数据源失败");
+        }
+    }
+
+    /**
+     * 创建JNDI数据源
+     *
+     * @param jndiName jndi数据源名称
+     * @return 数据源
+     */
+    public DataSource createJNDIDataSource(String jndiName) {
+        return JNDI_DATA_SOURCE_LOOKUP.getDataSource(jndiName);
+    }
+
+    /**
+     * 创建DRUID数据源
+     *
+     * @param dataSourceProperty 数据源参数
+     * @return 数据源
+     */
+    public DataSource createDruidDataSource(DataSourceProperty dataSourceProperty) {
+        DruidDataSource dataSource = new DruidDataSource();
+        dataSource.setName(dataSourceProperty.getPollName());
+        dataSource.setUrl(dataSourceProperty.getUrl());
+        dataSource.setUsername(dataSourceProperty.getUsername());
+        dataSource.setPassword(dataSourceProperty.getPassword());
+        dataSource.setDriverClassName(dataSourceProperty.getDriverClassName());
+
+        DruidConfig config = dataSourceProperty.getDruid();
+        dataSource.configFromPropety(config.toProperties(druidGlobalConfig));
+
+        //设置druid内置properties不支持的的参数
+        Boolean testOnReturn = config.getTestOnReturn() == null ? druidGlobalConfig.getTestOnReturn() : config.getTestOnReturn();
+        if (testOnReturn != null && testOnReturn.equals(true)) {
+            dataSource.setTestOnReturn(true);
+        }
+        Integer validationQueryTimeout = config.getValidationQueryTimeout() == null ? druidGlobalConfig.getValidationQueryTimeout() : config.getValidationQueryTimeout();
+        if (validationQueryTimeout != null && !validationQueryTimeout.equals(-1)) {
+            dataSource.setValidationQueryTimeout(validationQueryTimeout);
+        }
+
+        Boolean sharePreparedStatements = config.getSharePreparedStatements() == null ? druidGlobalConfig.getSharePreparedStatements() : config.getSharePreparedStatements();
+        if (sharePreparedStatements != null && sharePreparedStatements.equals(true)) {
+            dataSource.setSharePreparedStatements(true);
+        }
+        Integer connectionErrorRetryAttempts = config.getConnectionErrorRetryAttempts() == null ? druidGlobalConfig.getConnectionErrorRetryAttempts() : config.getConnectionErrorRetryAttempts();
+        if (connectionErrorRetryAttempts != null && !connectionErrorRetryAttempts.equals(1)) {
+            dataSource.setConnectionErrorRetryAttempts(connectionErrorRetryAttempts);
+        }
+        Boolean breakAfterAcquireFailure = config.getBreakAfterAcquireFailure() == null ? druidGlobalConfig.getBreakAfterAcquireFailure() : config.getBreakAfterAcquireFailure();
+        if (breakAfterAcquireFailure != null && breakAfterAcquireFailure.equals(true)) {
+            dataSource.setBreakAfterAcquireFailure(true);
+        }
+        try {
+            dataSource.init();
+        } catch (SQLException e) {
+            log.error("druid数据源启动失败", e);
+        }
+        return dataSource;
+    }
+
+    /**
+     * 创建Hikari数据源
+     *
+     * @param dataSourceProperty 数据源参数
+     * @return 数据源
+     * @author 离世庭院
+     */
+    public DataSource createHikariDataSource(DataSourceProperty dataSourceProperty) {
+        HikariConfig hikariConfig = dataSourceProperty.getHikari();
+        if (hikariConfig == null) {
+            //自己没设置就copy全局参数
+            hikariConfig.copyState(globalHikariConfig);
+        }
+        hikariConfig.setJdbcUrl(dataSourceProperty.getUrl());
+        hikariConfig.setUsername(dataSourceProperty.getUsername());
+        hikariConfig.setPassword(dataSourceProperty.getPassword());
+        hikariConfig.setDriverClassName(dataSourceProperty.getDriverClassName());
+        //暂时不支持设置监控
+//        hikariConfig.setMetricRegistry(metricRegistry);
+//        hikariConfig.setHealthCheckRegistry(healthCheckRegistry);
+        hikariConfig.setPoolName(dataSourceProperty.getPollName());
+        return new HikariDataSource(hikariConfig);
+    }
+}
diff --git a/src/main/java/com/baomidou/dynamic/datasource/DynamicGroupDataSource.java b/src/main/java/com/baomidou/dynamic/datasource/DynamicGroupDataSource.java
new file mode 100644
index 0000000..844cd58
--- /dev/null
+++ b/src/main/java/com/baomidou/dynamic/datasource/DynamicGroupDataSource.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright © 2018 organization baomidou
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ */
+package com.baomidou.dynamic.datasource;
+
+import com.baomidou.dynamic.datasource.strategy.DynamicDataSourceStrategy;
+import lombok.Data;
+
+import javax.sql.DataSource;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * 分组数据源
+ *
+ * @author TaoYu Kanyuxia
+ * @since 2.0.0
+ */
+@Data
+public class DynamicGroupDataSource {
+
+    /**
+     * 组名
+     */
+    private String groupName;
+
+    /**
+     * 数据源切换策略
+     */
+    private DynamicDataSourceStrategy dynamicDataSourceStrategy;
+
+    /**
+     * 当前组下所有数据源
+     */
+    private List dataSources = new LinkedList<>();
+
+    public DynamicGroupDataSource(String groupName, DynamicDataSourceStrategy dynamicDataSourceStrategy) {
+        this.groupName = groupName;
+        this.dynamicDataSourceStrategy = dynamicDataSourceStrategy;
+    }
+
+    public void addDatasource(DataSource dataSource) {
+        dataSources.add(dataSource);
+    }
+
+    public void removeDatasource(DataSource dataSource) {
+        dataSources.remove(dataSource);
+    }
+
+    public DataSource determineDataSource() {
+        return dynamicDataSourceStrategy.determineDataSource(dataSources);
+    }
+
+    public int size() {
+        return dataSources.size();
+    }
+}
diff --git a/src/main/java/com/baomidou/dynamic/datasource/DynamicRoutingDataSource.java b/src/main/java/com/baomidou/dynamic/datasource/DynamicRoutingDataSource.java
new file mode 100644
index 0000000..cced38c
--- /dev/null
+++ b/src/main/java/com/baomidou/dynamic/datasource/DynamicRoutingDataSource.java
@@ -0,0 +1,167 @@
+/**
+ * Copyright © 2018 organization baomidou
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ */
+package com.baomidou.dynamic.datasource;
+
+import com.baomidou.dynamic.datasource.provider.DynamicDataSourceProvider;
+import com.baomidou.dynamic.datasource.strategy.DynamicDataSourceStrategy;
+import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.util.StringUtils;
+
+import javax.sql.DataSource;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 核心动态数据源组件
+ *
+ * @author TaoYu Kanyuxia
+ * @since 1.0.0
+ */
+@Slf4j
+public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
+
+
+    private static final String UNDERLINE = "_";
+    @Setter
+    protected DynamicDataSourceProvider provider;
+    @Setter
+    protected Class strategy;
+    @Setter
+    protected String primary;
+    /**
+     * 所有数据库
+     */
+    private Map dataSourceMap = new LinkedHashMap<>();
+    /**
+     * 分组数据库
+     */
+    private Map groupDataSources = new ConcurrentHashMap<>();
+
+    @Override
+    public DataSource determineDataSource() {
+        return getDataSource(DynamicDataSourceContextHolder.getDataSourceLookupKey());
+    }
+
+    private DataSource determinePrimaryDataSource() {
+        log.debug("从默认数据源中返回数据");
+        return groupDataSources.containsKey(primary) ? groupDataSources.get(primary).determineDataSource() : dataSourceMap.get(primary);
+    }
+
+    /**
+     * 获取当前所有的数据源
+     *
+     * @return 当前所有数据源
+     */
+    public Map getCurrentDataSources() {
+        return dataSourceMap;
+    }
+
+    /**
+     * 获取的当前所有的分组数据源
+     *
+     * @return 当前所有的分组数据源
+     */
+    public Map getCurrentGroupDataSources() {
+        return groupDataSources;
+    }
+
+    /**
+     * 获取数据源
+     *
+     * @param ds 数据源名称
+     * @return 数据源
+     */
+    public DataSource getDataSource(String ds) {
+        if (StringUtils.isEmpty(ds)) {
+            return determinePrimaryDataSource();
+        } else if (!groupDataSources.isEmpty() && groupDataSources.containsKey(ds)) {
+            log.debug("从 {} 组数据源中返回数据源", ds);
+            return groupDataSources.get(ds).determineDataSource();
+        } else if (dataSourceMap.containsKey(ds)) {
+            log.debug("从 {} 单数据源中返回数据源", ds);
+            return dataSourceMap.get(ds);
+        }
+        return determinePrimaryDataSource();
+    }
+
+    /**
+     * 添加数据源
+     *
+     * @param ds         数据源名称
+     * @param dataSource 数据源
+     */
+    public synchronized void addDataSource(String ds, DataSource dataSource) {
+        dataSourceMap.put(ds, dataSource);
+        if (ds.contains(UNDERLINE)) {
+            String group = ds.split(UNDERLINE)[0];
+            if (groupDataSources.containsKey(group)) {
+                groupDataSources.get(group).addDatasource(dataSource);
+            } else {
+                try {
+                    DynamicGroupDataSource groupDatasource = new DynamicGroupDataSource(group, strategy.newInstance());
+                    groupDatasource.addDatasource(dataSource);
+                    groupDataSources.put(group, groupDatasource);
+                } catch (Exception e) {
+                    log.error("添加数据源失败", e);
+                    dataSourceMap.remove(ds);
+                }
+            }
+        }
+        log.info("动态数据源-加载 {} 成功", ds);
+    }
+
+    /**
+     * 删除数据源
+     *
+     * @param ds 数据源名称
+     */
+    public synchronized void removeDataSource(String ds) {
+        if (dataSourceMap.containsKey(ds)) {
+            DataSource dataSource = dataSourceMap.get(ds);
+            dataSourceMap.remove(ds);
+            if (ds.contains(UNDERLINE)) {
+                String group = ds.split(UNDERLINE)[0];
+                if (groupDataSources.containsKey(group)) {
+                    groupDataSources.get(group).removeDatasource(dataSource);
+                }
+            }
+            log.info("动态数据源-删除 {} 成功", ds);
+        } else {
+            log.warn("动态数据源-未找到 {} 数据源");
+        }
+    }
+
+    public void init() {
+        Map dataSources = provider.loadDataSources();
+        log.info("初始共加载 {} 个数据源", dataSources.size());
+        //添加并分组数据源
+        for (Map.Entry dsItem : dataSources.entrySet()) {
+            addDataSource(dsItem.getKey(), dsItem.getValue());
+        }
+        //检测默认数据源设置
+        if (groupDataSources.containsKey(primary)) {
+            log.info("当前的默认数据源是组数据源,组名为 {} ,其下有 {} 个数据源", primary, groupDataSources.size());
+        } else if (dataSourceMap.containsKey(primary)) {
+            log.info("当前的默认数据源是单数据源,数据源名为 {}", primary);
+        } else {
+            throw new RuntimeException("请检查primary默认数据库设置");
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/baomidou/dynamic/datasource/annotation/DS.java b/src/main/java/com/baomidou/dynamic/datasource/annotation/DS.java
new file mode 100644
index 0000000..dc91b32
--- /dev/null
+++ b/src/main/java/com/baomidou/dynamic/datasource/annotation/DS.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright © 2018 organization baomidou
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ */
+package com.baomidou.dynamic.datasource.annotation;
+
+
+import java.lang.annotation.*;
+
+/**
+ * 注解在类上或方法上来切换数据源
+ *
+ * @author TaoYu Kanyuxia
+ * @since 1.0.0
+ */
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface DS {
+
+    /**
+     * 组名或者具体数据源名称或者spel参数(#开头)
+     *
+     * @return 数据源名称
+     */
+    String value();
+}
\ No newline at end of file
diff --git a/src/main/java/com/baomidou/dynamic/datasource/aop/DynamicDataSourceAnnotationAdvisor.java b/src/main/java/com/baomidou/dynamic/datasource/aop/DynamicDataSourceAnnotationAdvisor.java
new file mode 100644
index 0000000..2af6bab
--- /dev/null
+++ b/src/main/java/com/baomidou/dynamic/datasource/aop/DynamicDataSourceAnnotationAdvisor.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright © 2018 organization baomidou
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ */
+package com.baomidou.dynamic.datasource.aop;
+
+import com.baomidou.dynamic.datasource.annotation.DS;
+import lombok.NonNull;
+import org.aopalliance.aop.Advice;
+import org.springframework.aop.Pointcut;
+import org.springframework.aop.support.AbstractPointcutAdvisor;
+import org.springframework.aop.support.ComposablePointcut;
+import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
+
+/**
+ * 动态数据源AOP织入
+ *
+ * @author TaoYu
+ * @since 1.2.0
+ */
+public class DynamicDataSourceAnnotationAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
+
+    private Advice advice;
+
+    private Pointcut pointcut;
+
+    public DynamicDataSourceAnnotationAdvisor(@NonNull DynamicDataSourceAnnotationInterceptor dynamicDataSourceAnnotationInterceptor) {
+        this.advice = dynamicDataSourceAnnotationInterceptor;
+        this.pointcut = buildPointcut();
+    }
+
+    @Override
+    public Pointcut getPointcut() {
+        return this.pointcut;
+    }
+
+    @Override
+    public Advice getAdvice() {
+        return this.advice;
+    }
+
+    @Override
+    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+        if (this.advice instanceof BeanFactoryAware) {
+            ((BeanFactoryAware) this.advice).setBeanFactory(beanFactory);
+        }
+    }
+
+    private Pointcut buildPointcut() {
+        Pointcut cpc = new AnnotationMatchingPointcut(DS.class, true);
+        Pointcut mpc = AnnotationMatchingPointcut.forMethodAnnotation(DS.class);
+        return new ComposablePointcut(cpc).union(mpc);
+    }
+}
diff --git a/src/main/java/com/baomidou/dynamic/datasource/aop/DynamicDataSourceAnnotationInterceptor.java b/src/main/java/com/baomidou/dynamic/datasource/aop/DynamicDataSourceAnnotationInterceptor.java
new file mode 100644
index 0000000..4a7229d
--- /dev/null
+++ b/src/main/java/com/baomidou/dynamic/datasource/aop/DynamicDataSourceAnnotationInterceptor.java
@@ -0,0 +1,74 @@
+/**
+ * Copyright © 2018 organization baomidou
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ */
+package com.baomidou.dynamic.datasource.aop;
+
+import com.baomidou.dynamic.datasource.DynamicDataSourceClassResolver;
+import com.baomidou.dynamic.datasource.annotation.DS;
+import com.baomidou.dynamic.datasource.spel.DynamicDataSourceSpelParser;
+import com.baomidou.dynamic.datasource.spel.DynamicDataSourceSpelResolver;
+import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
+import lombok.Setter;
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+import org.springframework.core.annotation.AnnotationUtils;
+
+import java.lang.reflect.Method;
+
+/**
+ * 动态数据源AOP核心拦截器
+ *
+ * @author TaoYu
+ * @since 1.2.0
+ */
+public class DynamicDataSourceAnnotationInterceptor implements MethodInterceptor {
+
+    /**
+     * SPEL参数标识
+     */
+    private static final String SPEL_PREFIX = "#";
+
+    @Setter
+    private DynamicDataSourceSpelResolver dynamicDataSourceSpelResolver;
+
+    @Setter
+    private DynamicDataSourceSpelParser dynamicDataSourceSpelParser;
+
+    private DynamicDataSourceClassResolver dynamicDataSourceClassResolver = new DynamicDataSourceClassResolver();
+
+    @Override
+    public Object invoke(MethodInvocation invocation) throws Throwable {
+        try {
+            DynamicDataSourceContextHolder.setDataSourceLookupKey(determineDatasource(invocation));
+            return invocation.proceed();
+        } finally {
+            DynamicDataSourceContextHolder.clearDataSourceLookupKey();
+        }
+    }
+
+    private String determineDatasource(MethodInvocation invocation) throws Throwable {
+        Method method = invocation.getMethod();
+        Class declaringClass = dynamicDataSourceClassResolver.targetClass(invocation);
+        DS ds = method.isAnnotationPresent(DS.class) ? method.getAnnotation(DS.class)
+                : AnnotationUtils.findAnnotation(declaringClass, DS.class);
+        String value = ds.value();
+        if (!value.isEmpty() && value.startsWith(SPEL_PREFIX)) {
+            String spelValue = dynamicDataSourceSpelParser.parse(invocation, value);
+            return dynamicDataSourceSpelResolver.resolve(spelValue);
+        }
+        return value;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/baomidou/dynamic/datasource/provider/AbstractJdbcDataSourceProvider.java b/src/main/java/com/baomidou/dynamic/datasource/provider/AbstractJdbcDataSourceProvider.java
new file mode 100644
index 0000000..7fd18aa
--- /dev/null
+++ b/src/main/java/com/baomidou/dynamic/datasource/provider/AbstractJdbcDataSourceProvider.java
@@ -0,0 +1,109 @@
+/**
+ * Copyright © 2018 organization baomidou
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ */
+package com.baomidou.dynamic.datasource.provider;
+
+import com.baomidou.dynamic.datasource.DynamicDataSourceCreator;
+import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
+import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.support.JdbcUtils;
+
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * JDBC数据源提供者(抽象)
+ *
+ * @author TaoYu
+ * @since 2.1.2
+ */
+@Slf4j
+public abstract class AbstractJdbcDataSourceProvider implements DynamicDataSourceProvider {
+
+    @Autowired(required = false)
+    protected DynamicDataSourceProperties dynamicDataSourceProperties;
+
+    @Autowired
+    private DynamicDataSourceCreator dynamicDataSourceCreator;
+
+    /**
+     * JDBC driver
+     */
+    private String driverClassName;
+    /**
+     * JDBC url 地址
+     */
+    private String url;
+    /**
+     * JDBC 用户名
+     */
+    private String username;
+    /**
+     * JDBC 密码
+     */
+    private String password;
+
+    public AbstractJdbcDataSourceProvider(String driverClassName, String url, String username, String password) {
+        this.driverClassName = driverClassName;
+        this.url = url;
+        this.username = username;
+        this.password = password;
+    }
+
+    @Override
+    public Map loadDataSources() {
+        Connection conn = null;
+        Statement stmt = null;
+        try {
+            Class.forName(driverClassName);
+            log.info("成功加载数据库驱动程序");
+            conn = DriverManager.getConnection(url, username, password);
+            log.info("成功获取数据库连接");
+            stmt = conn.createStatement();
+            Map dataSourcePropertiesMap = executeStmt(stmt);
+            Map dataSourceMap = new HashMap<>(dataSourcePropertiesMap.size());
+            for (Map.Entry item : dataSourcePropertiesMap.entrySet()) {
+                String pollName = item.getKey();
+                DataSourceProperty dataSourceProperty = item.getValue();
+                dataSourceProperty.setPollName(pollName);
+                dataSourceMap.put(pollName, dynamicDataSourceCreator.createDataSource(dataSourceProperty));
+            }
+            return dataSourceMap;
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            JdbcUtils.closeConnection(conn);
+            JdbcUtils.closeStatement(stmt);
+        }
+        return null;
+    }
+
+    /**
+     * 执行语句获得数据源参数
+     *
+     * @param statement 语句
+     * @return 数据源参数
+     * @throws SQLException sql异常
+     */
+    protected abstract Map executeStmt(Statement statement) throws SQLException;
+}
diff --git a/src/main/java/com/baomidou/dynamic/datasource/provider/DynamicDataSourceProvider.java b/src/main/java/com/baomidou/dynamic/datasource/provider/DynamicDataSourceProvider.java
new file mode 100644
index 0000000..70d8dd2
--- /dev/null
+++ b/src/main/java/com/baomidou/dynamic/datasource/provider/DynamicDataSourceProvider.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright © 2018 organization baomidou
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ */
+package com.baomidou.dynamic.datasource.provider;
+
+import javax.sql.DataSource;
+import java.util.Map;
+
+/**
+ * 多数据源加载接口,默认的实现为从yml信息中加载所有数据源
+ * 你可以自己实现从其他地方加载所有数据源
+ *
+ * @author TaoYu Kanyuxia
+ * @see YmlDynamicDataSourceProvider
+ * @see AbstractJdbcDataSourceProvider
+ * @since 1.0.0
+ */
+public interface DynamicDataSourceProvider {
+
+    /**
+     * 加载所有数据源
+     *
+     * @return 所有数据源,key为数据源名称
+     */
+    Map loadDataSources();
+}
diff --git a/src/main/java/com/baomidou/dynamic/datasource/provider/YmlDynamicDataSourceProvider.java b/src/main/java/com/baomidou/dynamic/datasource/provider/YmlDynamicDataSourceProvider.java
new file mode 100644
index 0000000..3a195f3
--- /dev/null
+++ b/src/main/java/com/baomidou/dynamic/datasource/provider/YmlDynamicDataSourceProvider.java
@@ -0,0 +1,63 @@
+/**
+ * Copyright © 2018 organization baomidou
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ */
+package com.baomidou.dynamic.datasource.provider;
+
+import com.baomidou.dynamic.datasource.DynamicDataSourceCreator;
+import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
+import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
+import lombok.extern.slf4j.Slf4j;
+
+import javax.sql.DataSource;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * YML数据源提供者
+ *
+ * @author TaoYu Kanyuxia
+ * @since 1.0.0
+ */
+@Slf4j
+public class YmlDynamicDataSourceProvider implements DynamicDataSourceProvider {
+
+    /**
+     * 多数据源参数
+     */
+    private DynamicDataSourceProperties properties;
+    /**
+     * 多数据源创建器
+     */
+    private DynamicDataSourceCreator dynamicDataSourceCreator;
+
+    public YmlDynamicDataSourceProvider(DynamicDataSourceProperties properties, DynamicDataSourceCreator dynamicDataSourceCreator) {
+        this.properties = properties;
+        this.dynamicDataSourceCreator = dynamicDataSourceCreator;
+    }
+
+    @Override
+    public Map loadDataSources() {
+        Map dataSourcePropertiesMap = properties.getDatasource();
+        Map dataSourceMap = new HashMap<>(dataSourcePropertiesMap.size());
+        for (Map.Entry item : dataSourcePropertiesMap.entrySet()) {
+            String pollName = item.getKey();
+            DataSourceProperty dataSourceProperty = item.getValue();
+            dataSourceProperty.setPollName(pollName);
+            dataSourceMap.put(pollName, dynamicDataSourceCreator.createDataSource(dataSourceProperty));
+        }
+        return dataSourceMap;
+    }
+}
diff --git a/src/main/java/com/baomidou/dynamic/datasource/spel/DefaultDynamicDataSourceSpelParser.java b/src/main/java/com/baomidou/dynamic/datasource/spel/DefaultDynamicDataSourceSpelParser.java
new file mode 100644
index 0000000..0801f2e
--- /dev/null
+++ b/src/main/java/com/baomidou/dynamic/datasource/spel/DefaultDynamicDataSourceSpelParser.java
@@ -0,0 +1,80 @@
+/**
+ * Copyright © 2018 organization baomidou
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ */
+package com.baomidou.dynamic.datasource.spel;
+
+import org.aopalliance.intercept.MethodInvocation;
+import org.springframework.context.expression.MethodBasedEvaluationContext;
+import org.springframework.core.DefaultParameterNameDiscoverer;
+import org.springframework.core.ParameterNameDiscoverer;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import java.lang.reflect.Method;
+
+/**
+ * 动态数据源Spel解析器
+ *
+ * @author TaoYu
+ * @since 2.3.0
+ */
+public class DefaultDynamicDataSourceSpelParser implements DynamicDataSourceSpelParser {
+
+    /**
+     * 参数发现器
+     */
+    private static final ParameterNameDiscoverer NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
+    /**
+     * Express语法解析器
+     */
+    private static final ExpressionParser PARSER = new SpelExpressionParser();
+    /**
+     * session开头
+     */
+    private static final String SESSION_PREFIX = "#session";
+    /**
+     * header开头
+     */
+    private static final String HEADER_PREFIX = "#header";
+
+
+    /**
+     * 解析多数据源spel的参数
+     *
+     * @param invocation 动态方法执行器
+     * @param key        需要解析的key
+     * @return 解析后的值
+     */
+    @Override
+    public String parse(MethodInvocation invocation, String key) {
+        if (key.startsWith(SESSION_PREFIX)) {
+            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+            return request.getSession().getAttribute(key.substring(9)).toString();
+        } else if (key.startsWith(HEADER_PREFIX)) {
+            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+            return request.getHeader(key.substring(8));
+        } else {
+            Method method = invocation.getMethod();
+            Object[] arguments = invocation.getArguments();
+            EvaluationContext context = new MethodBasedEvaluationContext(null, method, arguments, NAME_DISCOVERER);
+            return PARSER.parseExpression(key).getValue(context).toString();
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/baomidou/dynamic/datasource/spel/DefaultDynamicDataSourceSpelResolver.java b/src/main/java/com/baomidou/dynamic/datasource/spel/DefaultDynamicDataSourceSpelResolver.java
new file mode 100644
index 0000000..e57174d
--- /dev/null
+++ b/src/main/java/com/baomidou/dynamic/datasource/spel/DefaultDynamicDataSourceSpelResolver.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright © 2018 organization baomidou
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ */
+package com.baomidou.dynamic.datasource.spel;
+
+/**
+ * 默认的spel参数处理器
+ * 直接返回 spel处理后的参数
+ *
+ * @author TaoYu
+ * @since 2.3.0
+ */
+public class DefaultDynamicDataSourceSpelResolver implements DynamicDataSourceSpelResolver {
+
+    @Override
+    public String resolve(String spelValue) {
+        return spelValue;
+    }
+}
diff --git a/src/main/java/com/baomidou/dynamic/datasource/spel/DynamicDataSourceSpelParser.java b/src/main/java/com/baomidou/dynamic/datasource/spel/DynamicDataSourceSpelParser.java
new file mode 100644
index 0000000..42b8cf6
--- /dev/null
+++ b/src/main/java/com/baomidou/dynamic/datasource/spel/DynamicDataSourceSpelParser.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright © 2018 organization baomidou
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ */
+package com.baomidou.dynamic.datasource.spel;
+
+import org.aopalliance.intercept.MethodInvocation;
+
+/**
+ * 动态数据源SPEL解析器
+ *
+ * @author TaoYu
+ * @since 2.3.3
+ */
+public interface DynamicDataSourceSpelParser {
+
+    /**
+     * 解析SPEL
+     *
+     * @param invocation 动态代理方法对象
+     * @param key        ds的值
+     * @return 解析SPEL后获取的数据源名称
+     */
+    String parse(MethodInvocation invocation, String key);
+}
diff --git a/src/main/java/com/baomidou/dynamic/datasource/spel/DynamicDataSourceSpelResolver.java b/src/main/java/com/baomidou/dynamic/datasource/spel/DynamicDataSourceSpelResolver.java
new file mode 100644
index 0000000..3d2af12
--- /dev/null
+++ b/src/main/java/com/baomidou/dynamic/datasource/spel/DynamicDataSourceSpelResolver.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright © 2018 organization baomidou
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ */
+package com.baomidou.dynamic.datasource.spel;
+
+/**
+ * 动态数据源SPEL处理器
+ *
+ * @author TaoYu
+ * @since 2.3.0
+ */
+public interface DynamicDataSourceSpelResolver {
+
+    /**
+     * 处理spel解析后的参数
+     *
+     * @param spelValue spel参数
+     * @return 返回数据源名称
+     */
+    String resolve(String spelValue);
+}
diff --git a/src/main/java/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/DataSourceProperty.java b/src/main/java/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/DataSourceProperty.java
new file mode 100644
index 0000000..5081037
--- /dev/null
+++ b/src/main/java/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/DataSourceProperty.java
@@ -0,0 +1,74 @@
+/**
+ * Copyright © 2018 organization baomidou
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ */
+package com.baomidou.dynamic.datasource.spring.boot.autoconfigure;
+
+import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig;
+import com.zaxxer.hikari.HikariConfig;
+import lombok.Data;
+import lombok.experimental.Accessors;
+import org.springframework.boot.context.properties.NestedConfigurationProperty;
+
+import javax.sql.DataSource;
+
+/**
+ * @author TaoYu
+ * @since 1.2.0
+ */
+@Data
+@Accessors(chain = true)
+public class DataSourceProperty {
+
+    /**
+     * 连接池名称(只是一个名称标识)
+ * 默认是配置文件上的名称 + */ + private String pollName; + /** + * 连接池类型,如果不设置自动查找 Druid > HikariCp + */ + private Class type; + /** + * JDBC driver + */ + private String driverClassName; + /** + * JDBC url 地址 + */ + private String url; + /** + * JDBC 用户名 + */ + private String username; + /** + * JDBC 密码 + */ + private String password; + /** + * jndi数据源名称(设置即表示启用) + */ + private String jndiName; + /** + * Druid参数配置 + */ + @NestedConfigurationProperty + private DruidConfig druid = new DruidConfig(); + /** + * HikariCp参数配置 + */ + @NestedConfigurationProperty + private HikariConfig hikari = new HikariConfig(); +} diff --git a/src/main/java/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/DynamicDataSourceAutoConfiguration.java b/src/main/java/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/DynamicDataSourceAutoConfiguration.java new file mode 100644 index 0000000..08fa122 --- /dev/null +++ b/src/main/java/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/DynamicDataSourceAutoConfiguration.java @@ -0,0 +1,120 @@ +/** + * Copyright © 2018 organization baomidou + *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ */
+package com.baomidou.dynamic.datasource.spring.boot.autoconfigure;
+
+import com.baomidou.dynamic.datasource.DynamicDataSourceCreator;
+import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
+import com.baomidou.dynamic.datasource.aop.DynamicDataSourceAnnotationAdvisor;
+import com.baomidou.dynamic.datasource.aop.DynamicDataSourceAnnotationInterceptor;
+import com.baomidou.dynamic.datasource.provider.DynamicDataSourceProvider;
+import com.baomidou.dynamic.datasource.provider.YmlDynamicDataSourceProvider;
+import com.baomidou.dynamic.datasource.spel.DefaultDynamicDataSourceSpelParser;
+import com.baomidou.dynamic.datasource.spel.DefaultDynamicDataSourceSpelResolver;
+import com.baomidou.dynamic.datasource.spel.DynamicDataSourceSpelParser;
+import com.baomidou.dynamic.datasource.spel.DynamicDataSourceSpelResolver;
+import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidDynamicDataSourceConfiguration;
+import com.baomidou.dynamic.datasource.strategy.DynamicDataSourceStrategy;
+import com.p6spy.engine.spy.P6DataSource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+import javax.sql.DataSource;
+
+/**
+ * 动态数据源核心自动配置类
+ *
+ * @author TaoYu Kanyuxia
+ * @see DynamicDataSourceProvider
+ * @see DynamicDataSourceStrategy
+ * @see DynamicRoutingDataSource
+ * @since 1.0.0
+ */
+@Slf4j
+@Configuration
+@EnableConfigurationProperties(DynamicDataSourceProperties.class)
+@AutoConfigureBefore(DataSourceAutoConfiguration.class)
+@Import(DruidDynamicDataSourceConfiguration.class)
+public class DynamicDataSourceAutoConfiguration {
+
+    @Autowired
+    private DynamicDataSourceProperties properties;
+
+    @Bean
+    @ConditionalOnMissingBean
+    public DynamicDataSourceProvider dynamicDataSourceProvider(DynamicDataSourceCreator dynamicDataSourceCreator) {
+        return new YmlDynamicDataSourceProvider(properties, dynamicDataSourceCreator);
+    }
+
+    @Bean
+    @ConditionalOnMissingBean
+    public DynamicDataSourceCreator dynamicDataSourceCreator() {
+        DynamicDataSourceCreator dynamicDataSourceCreator = new DynamicDataSourceCreator();
+        dynamicDataSourceCreator.setDruidGlobalConfig(properties.getDruid());
+        dynamicDataSourceCreator.setGlobalHikariConfig(properties.getHikari());
+        return dynamicDataSourceCreator;
+    }
+
+    @Bean
+    @ConditionalOnMissingBean
+    public DataSource dataSource(DynamicDataSourceProvider dynamicDataSourceProvider) {
+        DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();
+        dataSource.setPrimary(properties.getPrimary());
+        dataSource.setStrategy(properties.getStrategy());
+        dataSource.setProvider(dynamicDataSourceProvider);
+        dataSource.init();
+        if (properties.getP6spy()) {
+            try {
+                Class.forName("com.p6spy.engine.spy.P6DataSource");
+                log.info("动态数据源-关联p6sy成功");
+                return new P6DataSource(dataSource);
+            } catch (Exception e) {
+                log.warn("多数据源启动器开启了p6spy但并未引入相关依赖");
+            }
+        }
+        return dataSource;
+    }
+
+    @Bean
+    @ConditionalOnMissingBean
+    public DynamicDataSourceAnnotationAdvisor dynamicDatasourceAnnotationAdvisor(DynamicDataSourceSpelParser dynamicDataSourceSpelParser, DynamicDataSourceSpelResolver dynamicDataSourceSpelResolver) {
+        DynamicDataSourceAnnotationInterceptor interceptor = new DynamicDataSourceAnnotationInterceptor();
+        interceptor.setDynamicDataSourceSpelParser(dynamicDataSourceSpelParser);
+        interceptor.setDynamicDataSourceSpelResolver(dynamicDataSourceSpelResolver);
+        DynamicDataSourceAnnotationAdvisor advisor = new DynamicDataSourceAnnotationAdvisor(interceptor);
+        advisor.setOrder(properties.getOrder());
+        return advisor;
+    }
+
+    @Bean
+    @ConditionalOnMissingBean
+    public DynamicDataSourceSpelParser dynamicDataSourceSpelParser() {
+        return new DefaultDynamicDataSourceSpelParser();
+    }
+
+    @Bean
+    @ConditionalOnMissingBean
+    public DynamicDataSourceSpelResolver dynamicDataSourceSpelResolver() {
+        return new DefaultDynamicDataSourceSpelResolver();
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/DynamicDataSourceProperties.java b/src/main/java/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/DynamicDataSourceProperties.java
new file mode 100644
index 0000000..50b7a2a
--- /dev/null
+++ b/src/main/java/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/DynamicDataSourceProperties.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright © 2018 organization baomidou
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ */
+package com.baomidou.dynamic.datasource.spring.boot.autoconfigure;
+
+import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig;
+import com.baomidou.dynamic.datasource.strategy.DynamicDataSourceStrategy;
+import com.baomidou.dynamic.datasource.strategy.LoadBalanceDynamicDataSourceStrategy;
+import com.zaxxer.hikari.HikariConfig;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.context.properties.NestedConfigurationProperty;
+import org.springframework.core.Ordered;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * DynamicDataSourceProperties
+ *
+ * @author TaoYu Kanyuxia
+ * @see DataSourceProperties
+ * @since 1.0.0
+ */
+@Getter
+@Setter
+@ConfigurationProperties(prefix = "spring.datasource.dynamic")
+public class DynamicDataSourceProperties {
+
+    /**
+     * 必须设置默认的库,默认master
+     */
+    private String primary = "master";
+    /**
+     * 每一个数据源
+     */
+    private Map datasource = new LinkedHashMap<>();
+    /**
+     * 多数据源选择算法clazz,默认负载均衡算法
+     */
+    private Class strategy = LoadBalanceDynamicDataSourceStrategy.class;
+    /**
+     * aop切面顺序,默认优先级最高
+     */
+    private Integer order = Ordered.HIGHEST_PRECEDENCE;
+    /**
+     * 是否使用p6spy输出,默认不输出
+     */
+    private Boolean p6spy = false;
+    /**
+     * Druid全局参数配置
+     */
+    @NestedConfigurationProperty
+    private DruidConfig druid = new DruidConfig();
+    /**
+     * HikariCp全局参数配置
+     */
+    @NestedConfigurationProperty
+    private HikariConfig hikari = new HikariConfig();
+}
diff --git a/src/main/java/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/druid/DruidConfig.java b/src/main/java/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/druid/DruidConfig.java
new file mode 100644
index 0000000..c12c22a
--- /dev/null
+++ b/src/main/java/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/druid/DruidConfig.java
@@ -0,0 +1,244 @@
+/**
+ * Copyright © 2018 organization baomidou
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ */
+package com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Properties;
+
+import static com.alibaba.druid.pool.DruidAbstractDataSource.*;
+
+/**
+ * Druid常用参数
+ *
+ * @author TaoYu
+ * @since 1.2.0
+ */
+@Data
+@Accessors(chain = true)
+@NoArgsConstructor
+@Slf4j
+public class DruidConfig {
+
+    private Integer initialSize;
+    private Integer maxActive;
+    private Integer minIdle;
+    private Long maxWait;
+    private Long timeBetweenEvictionRunsMillis;
+    private Long timeBetweenLogStatsMillis;
+    private Integer statSqlMaxSize;
+    private Long minEvictableIdleTimeMillis;
+    private Long maxEvictableIdleTimeMillis;
+    private Boolean testWhileIdle;
+    private Boolean testOnBorrow;
+    private Boolean testOnReturn;
+    private String validationQuery;
+    private Integer validationQueryTimeout;
+    private Boolean useGlobalDataSourceStat;
+    private Boolean asyncInit;
+    private String filters;
+    private Boolean clearFiltersEnable;
+    private Boolean resetStatEnable;
+    private Integer notFullTimeoutRetryCount;
+    private Long maxWaitThreadCount;
+    private Boolean failFast;
+    private Integer phyTimeoutMillis;
+    private Boolean keepAlive;
+    private Boolean poolPreparedStatements;
+    private Boolean initVariants;
+    private Boolean initGlobalVariants;
+    private Boolean useUnfairLock;
+    private Boolean killWhenSocketReadTimeout;
+    private String connectionProperties;
+    private Integer maxPoolPreparedStatementPerConnectionSize;
+    private String initConnectionSqls;
+    private Boolean sharePreparedStatements;
+    private Integer connectionErrorRetryAttempts;
+    private Boolean breakAfterAcquireFailure;
+
+    private String publicKey;
+
+    public Properties toProperties(DruidConfig config) {
+        Properties properties = new Properties();
+        Integer initialSize = this.initialSize == null ? config.getInitialSize() : this.initialSize;
+        if (initialSize != null && !initialSize.equals(DEFAULT_INITIAL_SIZE)) {
+            properties.setProperty("druid.initialSize", String.valueOf(initialSize));
+        }
+
+        Integer maxActive = this.maxActive == null ? config.getMaxActive() : this.maxActive;
+        if (maxActive != null && !maxActive.equals(DEFAULT_MAX_WAIT)) {
+            properties.setProperty("druid.maxActive", String.valueOf(maxActive));
+        }
+
+        Integer minIdle = this.minIdle == null ? config.getMinIdle() : this.minIdle;
+        if (minIdle != null && !minIdle.equals(DEFAULT_MIN_IDLE)) {
+            properties.setProperty("druid.minIdle", String.valueOf(minIdle));
+        }
+
+        Long maxWait = this.maxWait == null ? config.getMaxWait() : this.maxWait;
+        if (maxWait != null && !maxWait.equals(DEFAULT_MAX_WAIT)) {
+            properties.setProperty("druid.maxWait", String.valueOf(maxWait));
+        }
+
+        Long timeBetweenEvictionRunsMillis = this.timeBetweenEvictionRunsMillis == null ? config.getTimeBetweenEvictionRunsMillis() : this.timeBetweenEvictionRunsMillis;
+        if (timeBetweenEvictionRunsMillis != null && !timeBetweenEvictionRunsMillis.equals(DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS)) {
+            properties.setProperty("druid.timeBetweenEvictionRunsMillis", String.valueOf(timeBetweenEvictionRunsMillis));
+        }
+
+        Long timeBetweenLogStatsMillis = this.timeBetweenLogStatsMillis == null ? config.getTimeBetweenLogStatsMillis() : this.timeBetweenLogStatsMillis;
+        if (timeBetweenLogStatsMillis != null && timeBetweenLogStatsMillis > 0) {
+            properties.setProperty("druid.timeBetweenLogStatsMillis", String.valueOf(timeBetweenLogStatsMillis));
+        }
+
+        Integer statSqlMaxSize = this.statSqlMaxSize == null ? config.getStatSqlMaxSize() : this.statSqlMaxSize;
+        if (statSqlMaxSize != null) {
+            properties.setProperty("druid.stat.sql.MaxSize", String.valueOf(statSqlMaxSize));
+        }
+
+        Long minEvictableIdleTimeMillis = this.minEvictableIdleTimeMillis == null ? config.getMinEvictableIdleTimeMillis() : this.minEvictableIdleTimeMillis;
+        if (minEvictableIdleTimeMillis != null && !minEvictableIdleTimeMillis.equals(DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS)) {
+            properties.setProperty("druid.minEvictableIdleTimeMillis", String.valueOf(minEvictableIdleTimeMillis));
+        }
+
+        Long maxEvictableIdleTimeMillis = this.maxEvictableIdleTimeMillis == null ? config.getMaxEvictableIdleTimeMillis() : this.maxEvictableIdleTimeMillis;
+        if (maxEvictableIdleTimeMillis != null && !maxEvictableIdleTimeMillis.equals(DEFAULT_MAX_EVICTABLE_IDLE_TIME_MILLIS)) {
+            properties.setProperty("druid.maxEvictableIdleTimeMillis", String.valueOf(maxEvictableIdleTimeMillis));
+        }
+
+        Boolean testWhileIdle = this.testWhileIdle == null ? config.getTestWhileIdle() : this.testWhileIdle;
+        if (testWhileIdle != null && !testWhileIdle.equals(DEFAULT_WHILE_IDLE)) {
+            properties.setProperty("druid.testWhileIdle", "false");
+        }
+
+        Boolean testOnBorrow = this.testOnBorrow == null ? config.getTestOnBorrow() : this.testOnBorrow;
+        if (testOnBorrow != null && !testOnBorrow.equals(DEFAULT_TEST_ON_BORROW)) {
+            properties.setProperty("druid.testOnBorrow", "true");
+        }
+
+        String validationQuery = this.validationQuery == null ? config.getValidationQuery() : this.validationQuery;
+        if (validationQuery != null && validationQuery.length() > 0) {
+            properties.setProperty("druid.validationQuery", validationQuery);
+        }
+
+        Boolean useGlobalDataSourceStat = this.useGlobalDataSourceStat == null ? config.getUseGlobalDataSourceStat() : this.useGlobalDataSourceStat;
+        if (useGlobalDataSourceStat != null && useGlobalDataSourceStat.equals(Boolean.TRUE)) {
+            properties.setProperty("druid.useGlobalDataSourceStat", "true");
+        }
+
+        Boolean asyncInit = this.asyncInit == null ? config.getAsyncInit() : this.asyncInit;
+        if (asyncInit != null && asyncInit.equals(Boolean.TRUE)) {
+            properties.setProperty("druid.asyncInit", "true");
+        }
+
+        //filters单独处理,默认了stat,wall
+        String filters = this.filters == null ? config.getFilters() : this.filters;
+        if (filters == null) {
+            filters = "stat,wall";
+        }
+        if (publicKey != null && publicKey.length() > 0 && !filters.contains("config")) {
+            filters += ",config";
+        }
+        properties.setProperty("druid.filters", filters);
+
+        Boolean clearFiltersEnable = this.clearFiltersEnable == null ? config.getClearFiltersEnable() : this.clearFiltersEnable;
+        if (clearFiltersEnable != null && clearFiltersEnable.equals(Boolean.FALSE)) {
+            properties.setProperty("druid.clearFiltersEnable", "false");
+        }
+
+        Boolean resetStatEnable = this.resetStatEnable == null ? config.getResetStatEnable() : this.resetStatEnable;
+        if (resetStatEnable != null && resetStatEnable.equals(Boolean.FALSE)) {
+            properties.setProperty("druid.resetStatEnable", "false");
+        }
+
+        Integer notFullTimeoutRetryCount = this.notFullTimeoutRetryCount == null ? config.getNotFullTimeoutRetryCount() : this.notFullTimeoutRetryCount;
+        if (notFullTimeoutRetryCount != null && !notFullTimeoutRetryCount.equals(0)) {
+            properties.setProperty("druid.notFullTimeoutRetryCount", String.valueOf(notFullTimeoutRetryCount));
+        }
+
+        Long maxWaitThreadCount = this.maxWaitThreadCount == null ? config.getMaxWaitThreadCount() : this.maxWaitThreadCount;
+        if (maxWaitThreadCount != null && !maxWaitThreadCount.equals(DEFAULT_MAX_WAIT)) {
+            properties.setProperty("druid.maxWaitThreadCount", String.valueOf(maxWaitThreadCount));
+        }
+
+        Boolean failFast = this.failFast == null ? config.getFailFast() : this.failFast;
+        if (failFast != null && failFast.equals(Boolean.TRUE)) {
+            properties.setProperty("druid.failFast", "true");
+        }
+
+        Integer phyTimeoutMillis = this.phyTimeoutMillis == null ? config.getPhyTimeoutMillis() : this.phyTimeoutMillis;
+        if (phyTimeoutMillis != null && !phyTimeoutMillis.equals(DEFAULT_PHY_TIMEOUT_MILLIS)) {
+            properties.setProperty("druid.phyTimeoutMillis", String.valueOf(phyTimeoutMillis));
+        }
+
+        Boolean keepAlive = this.keepAlive == null ? config.getKeepAlive() : this.keepAlive;
+        if (keepAlive != null && keepAlive.equals(Boolean.TRUE)) {
+            properties.setProperty("druid.keepAlive", "true");
+        }
+
+        Boolean poolPreparedStatements = this.poolPreparedStatements == null ? config.getPoolPreparedStatements() : this.poolPreparedStatements;
+        if (poolPreparedStatements != null && poolPreparedStatements.equals(Boolean.TRUE)) {
+            properties.setProperty("druid.poolPreparedStatements", "true");
+        }
+
+        Boolean initVariants = this.initVariants == null ? config.getInitVariants() : this.initVariants;
+        if (initVariants != null && initVariants.equals(Boolean.TRUE)) {
+            properties.setProperty("druid.initVariants", "true");
+        }
+
+        Boolean initGlobalVariants = this.initGlobalVariants == null ? config.getInitGlobalVariants() : this.initGlobalVariants;
+        if (initGlobalVariants != null && initGlobalVariants.equals(Boolean.TRUE)) {
+            properties.setProperty("druid.initGlobalVariants", "true");
+        }
+
+        Boolean useUnfairLock = this.useUnfairLock == null ? config.getUseUnfairLock() : this.useUnfairLock;
+        if (useUnfairLock != null) {
+            properties.setProperty("druid.useUnfairLock", String.valueOf(useUnfairLock));
+        }
+
+        Boolean killWhenSocketReadTimeout = this.killWhenSocketReadTimeout == null ? config.getKillWhenSocketReadTimeout() : this.killWhenSocketReadTimeout;
+        if (killWhenSocketReadTimeout != null && killWhenSocketReadTimeout.equals(Boolean.TRUE)) {
+            properties.setProperty("druid.killWhenSocketReadTimeout", "true");
+        }
+
+        String connectProperties = this.connectionProperties == null ? config.getConnectionProperties() : this.connectionProperties;
+
+        if (this.publicKey != null && this.publicKey.length() > 0) {
+            if (connectProperties == null) {
+                connectProperties = "";
+            }
+            log.info("动态数据源-检测到您配置了druid加密,加密所需连接参数已为您自动配置");
+            connectProperties += "config.decrypt=true;config.decrypt.key=" + this.publicKey;
+        }
+        if (connectProperties != null && connectProperties.length() > 0) {
+            properties.setProperty("druid.connectProperties", connectProperties);
+        }
+
+        Integer maxPoolPreparedStatementPerConnectionSize = this.maxPoolPreparedStatementPerConnectionSize == null ? config.getMaxPoolPreparedStatementPerConnectionSize() : this.maxPoolPreparedStatementPerConnectionSize;
+        if (maxPoolPreparedStatementPerConnectionSize != null && !maxPoolPreparedStatementPerConnectionSize.equals(10)) {
+            properties.setProperty("druid.maxPoolPreparedStatementPerConnectionSize", String.valueOf(maxPoolPreparedStatementPerConnectionSize));
+        }
+
+        String initConnectionSqls = this.initConnectionSqls == null ? config.getInitConnectionSqls() : this.initConnectionSqls;
+        if (initConnectionSqls != null && initConnectionSqls.length() > 0) {
+            properties.setProperty("druid.initConnectionSqls", initConnectionSqls);
+        }
+        return properties;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/druid/DruidDynamicDataSourceConfiguration.java b/src/main/java/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/druid/DruidDynamicDataSourceConfiguration.java
new file mode 100644
index 0000000..b783e47
--- /dev/null
+++ b/src/main/java/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/druid/DruidDynamicDataSourceConfiguration.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright © 2018 organization baomidou
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ */
+package com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid;
+
+import com.alibaba.druid.pool.DruidDataSource;
+import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
+import com.alibaba.druid.spring.boot.autoconfigure.stat.DruidFilterConfiguration;
+import com.alibaba.druid.spring.boot.autoconfigure.stat.DruidSpringAopConfiguration;
+import com.alibaba.druid.spring.boot.autoconfigure.stat.DruidStatViewServletConfiguration;
+import com.alibaba.druid.spring.boot.autoconfigure.stat.DruidWebStatFilterConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+/**
+ * 从原生DruidDataSourceAutoConfigure复制
+ *
+ * @author TaoYu
+ * @since 1.1.0
+ */
+@Configuration
+@ConditionalOnClass(DruidDataSource.class)
+@EnableConfigurationProperties({DruidStatProperties.class})
+@Import({
+        DruidSpringAopConfiguration.class,
+        DruidStatViewServletConfiguration.class,
+        DruidWebStatFilterConfiguration.class,
+        DruidFilterConfiguration.class})
+public class DruidDynamicDataSourceConfiguration {
+
+}
diff --git a/src/main/java/com/baomidou/dynamic/datasource/strategy/DynamicDataSourceStrategy.java b/src/main/java/com/baomidou/dynamic/datasource/strategy/DynamicDataSourceStrategy.java
new file mode 100644
index 0000000..311d1c4
--- /dev/null
+++ b/src/main/java/com/baomidou/dynamic/datasource/strategy/DynamicDataSourceStrategy.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright © 2018 organization baomidou
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ */
+package com.baomidou.dynamic.datasource.strategy;
+
+import javax.sql.DataSource;
+import java.util.List;
+
+/**
+ * 多数据源选择策略接口
+ * 一般默认为负载均衡策略,默认提供了一个随机策略
+ *
+ * @author TaoYu Kanyuxia
+ * @see RandomDynamicDataSourceStrategy
+ * @see LoadBalanceDynamicDataSourceStrategy
+ * @since 1.0.0
+ */
+public interface DynamicDataSourceStrategy {
+
+    /**
+     * 决定当前数据源
+     *
+     * @param dataSources 数据源选择库
+     * @return dataSource 所选择的数据源
+     */
+    DataSource determineDataSource(List dataSources);
+}
diff --git a/src/main/java/com/baomidou/dynamic/datasource/strategy/LoadBalanceDynamicDataSourceStrategy.java b/src/main/java/com/baomidou/dynamic/datasource/strategy/LoadBalanceDynamicDataSourceStrategy.java
new file mode 100644
index 0000000..310b4dd
--- /dev/null
+++ b/src/main/java/com/baomidou/dynamic/datasource/strategy/LoadBalanceDynamicDataSourceStrategy.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright © 2018 organization baomidou
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ */
+package com.baomidou.dynamic.datasource.strategy;
+
+import javax.sql.DataSource;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * 负载均衡策略
+ *
+ * @author TaoYu Kanyuxia
+ * @since 1.0.0
+ */
+public class LoadBalanceDynamicDataSourceStrategy implements DynamicDataSourceStrategy {
+
+    /**
+     * 负载均衡计数器
+     */
+    private AtomicInteger index = new AtomicInteger(0);
+
+    @Override
+    public DataSource determineDataSource(List dataSources) {
+        return dataSources.get(Math.abs(index.getAndAdd(1)) % dataSources.size());
+    }
+}
diff --git a/src/main/java/com/baomidou/dynamic/datasource/strategy/RandomDynamicDataSourceStrategy.java b/src/main/java/com/baomidou/dynamic/datasource/strategy/RandomDynamicDataSourceStrategy.java
new file mode 100644
index 0000000..b44833c
--- /dev/null
+++ b/src/main/java/com/baomidou/dynamic/datasource/strategy/RandomDynamicDataSourceStrategy.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright © 2018 organization baomidou
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ */
+package com.baomidou.dynamic.datasource.strategy;
+
+import javax.sql.DataSource;
+import java.util.List;
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ * 随机策略
+ *
+ * @author TaoYu Kanyuxia
+ * @since 1.0.0
+ */
+public class RandomDynamicDataSourceStrategy implements DynamicDataSourceStrategy {
+
+    @Override
+    public DataSource determineDataSource(List dataSources) {
+        return dataSources.get(ThreadLocalRandom.current().nextInt(dataSources.size()));
+    }
+}
diff --git a/src/main/java/com/baomidou/dynamic/datasource/toolkit/DynamicDataSourceContextHolder.java b/src/main/java/com/baomidou/dynamic/datasource/toolkit/DynamicDataSourceContextHolder.java
new file mode 100644
index 0000000..4bdb1a4
--- /dev/null
+++ b/src/main/java/com/baomidou/dynamic/datasource/toolkit/DynamicDataSourceContextHolder.java
@@ -0,0 +1,82 @@
+/**
+ * Copyright © 2018 organization baomidou
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ */
+package com.baomidou.dynamic.datasource.toolkit;
+
+import java.util.concurrent.LinkedBlockingDeque;
+
+/**
+ * 核心基于ThreadLocal的切换数据源工具类
+ *
+ * @author TaoYu Kanyuxia
+ * @since 1.0.0
+ */
+public final class DynamicDataSourceContextHolder {
+
+    /**
+     * 为什么要用链表存储(准确的是栈)
+     * 
+     * 为了支持嵌套切换,如ABC三个service都是不同的数据源
+     * 其中A的某个业务要调B的方法,B的方法需要调用C的方法。一级一级调用切换,形成了链。
+     * 传统的只设置当前线程的方式不能满足此业务需求,必须模拟栈,后进先出。
+     * 
+ */ + @SuppressWarnings("unchecked") + private static final ThreadLocal> LOOKUP_KEY_HOLDER = new ThreadLocal() { + @Override + protected Object initialValue() { + return new LinkedBlockingDeque(); + } + }; + + private DynamicDataSourceContextHolder() { + } + + /** + * 获得当前线程数据源 + * + * @return 数据源名称 + */ + public static String getDataSourceLookupKey() { + LinkedBlockingDeque deque = LOOKUP_KEY_HOLDER.get(); + return deque.isEmpty() ? null : deque.getFirst(); + } + + /** + * 设置当前线程数据源 + * + * @param dataSourceLookupKey 数据源名称 + */ + public static void setDataSourceLookupKey(String dataSourceLookupKey) { + LOOKUP_KEY_HOLDER.get().addFirst(dataSourceLookupKey); + } + + /** + * 清空当前线程数据源 + *

+ * 如果当前线程是连续切换数据源 + * 只会移除掉当前线程的数据源名称 + *

+ */ + public static void clearDataSourceLookupKey() { + LinkedBlockingDeque deque = LOOKUP_KEY_HOLDER.get(); + if (deque.isEmpty()) { + LOOKUP_KEY_HOLDER.remove(); + } else { + deque.pollFirst(); + } + } +} diff --git a/src/main/resources/META-INF/spring.factories b/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000..6d4b5af --- /dev/null +++ b/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration \ No newline at end of file diff --git a/target/classes/META-INF/spring-configuration-metadata.json b/target/classes/META-INF/spring-configuration-metadata.json new file mode 100644 index 0000000..2e39106 --- /dev/null +++ b/target/classes/META-INF/spring-configuration-metadata.json @@ -0,0 +1,410 @@ +{ + "hints": [], + "groups": [ + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties", + "name": "spring.datasource.dynamic", + "type": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties", + "name": "spring.datasource.dynamic.druid", + "type": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties", + "name": "spring.datasource.dynamic.hikari", + "type": "com.zaxxer.hikari.HikariConfig" + } + ], + "properties": [ + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties", + "name": "spring.datasource.dynamic.datasource", + "description": "每一个数据源", + "type": "java.util.Map" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.async-init", + "type": "java.lang.Boolean" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.break-after-acquire-failure", + "type": "java.lang.Boolean" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.clear-filters-enable", + "type": "java.lang.Boolean" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.connection-error-retry-attempts", + "type": "java.lang.Integer" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.connection-properties", + "type": "java.lang.String" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.fail-fast", + "type": "java.lang.Boolean" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.filters", + "type": "java.lang.String" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.init-connection-sqls", + "type": "java.lang.String" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.init-global-variants", + "type": "java.lang.Boolean" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.init-variants", + "type": "java.lang.Boolean" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.initial-size", + "type": "java.lang.Integer" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.keep-alive", + "type": "java.lang.Boolean" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.kill-when-socket-read-timeout", + "type": "java.lang.Boolean" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.max-active", + "type": "java.lang.Integer" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.max-evictable-idle-time-millis", + "type": "java.lang.Long" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.max-pool-prepared-statement-per-connection-size", + "type": "java.lang.Integer" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.max-wait", + "type": "java.lang.Long" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.max-wait-thread-count", + "type": "java.lang.Long" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.min-evictable-idle-time-millis", + "type": "java.lang.Long" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.min-idle", + "type": "java.lang.Integer" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.not-full-timeout-retry-count", + "type": "java.lang.Integer" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.phy-timeout-millis", + "type": "java.lang.Integer" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.pool-prepared-statements", + "type": "java.lang.Boolean" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.public-key", + "type": "java.lang.String" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.reset-stat-enable", + "type": "java.lang.Boolean" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.share-prepared-statements", + "type": "java.lang.Boolean" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.stat-sql-max-size", + "type": "java.lang.Integer" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.test-on-borrow", + "type": "java.lang.Boolean" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.test-on-return", + "type": "java.lang.Boolean" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.test-while-idle", + "type": "java.lang.Boolean" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.time-between-eviction-runs-millis", + "type": "java.lang.Long" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.time-between-log-stats-millis", + "type": "java.lang.Long" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.use-global-data-source-stat", + "type": "java.lang.Boolean" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.use-unfair-lock", + "type": "java.lang.Boolean" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.validation-query", + "type": "java.lang.String" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig", + "name": "spring.datasource.dynamic.druid.validation-query-timeout", + "type": "java.lang.Integer" + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "name": "spring.datasource.dynamic.hikari.allow-pool-suspension", + "type": "java.lang.Boolean" + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "name": "spring.datasource.dynamic.hikari.auto-commit", + "type": "java.lang.Boolean" + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "name": "spring.datasource.dynamic.hikari.catalog", + "type": "java.lang.String" + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "name": "spring.datasource.dynamic.hikari.connection-init-sql", + "type": "java.lang.String" + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "name": "spring.datasource.dynamic.hikari.connection-test-query", + "type": "java.lang.String" + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "name": "spring.datasource.dynamic.hikari.connection-timeout", + "type": "java.lang.Long" + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "name": "spring.datasource.dynamic.hikari.data-source-class-name", + "type": "java.lang.String" + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "name": "spring.datasource.dynamic.hikari.data-source-j-n-d-i", + "type": "java.lang.String" + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "name": "spring.datasource.dynamic.hikari.data-source-properties", + "type": "java.util.Properties" + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "name": "spring.datasource.dynamic.hikari.driver-class-name", + "type": "java.lang.String" + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "name": "spring.datasource.dynamic.hikari.health-check-properties", + "type": "java.util.Properties" + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "name": "spring.datasource.dynamic.hikari.health-check-registry", + "type": "java.lang.Object" + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "name": "spring.datasource.dynamic.hikari.idle-timeout", + "type": "java.lang.Long" + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "deprecated": true, + "name": "spring.datasource.dynamic.hikari.initialization-fail-fast", + "type": "java.lang.Boolean", + "deprecation": {} + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "name": "spring.datasource.dynamic.hikari.initialization-fail-timeout", + "type": "java.lang.Long" + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "name": "spring.datasource.dynamic.hikari.isolate-internal-queries", + "type": "java.lang.Boolean" + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "name": "spring.datasource.dynamic.hikari.jdbc-url", + "type": "java.lang.String" + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "deprecated": true, + "name": "spring.datasource.dynamic.hikari.jdbc4-connection-test", + "type": "java.lang.Boolean", + "deprecation": {} + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "name": "spring.datasource.dynamic.hikari.leak-detection-threshold", + "type": "java.lang.Long" + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "name": "spring.datasource.dynamic.hikari.max-lifetime", + "type": "java.lang.Long" + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "name": "spring.datasource.dynamic.hikari.maximum-pool-size", + "type": "java.lang.Integer" + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "name": "spring.datasource.dynamic.hikari.metric-registry", + "type": "java.lang.Object" + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "name": "spring.datasource.dynamic.hikari.metrics-tracker-factory", + "type": "com.zaxxer.hikari.metrics.MetricsTrackerFactory" + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "name": "spring.datasource.dynamic.hikari.minimum-idle", + "type": "java.lang.Integer" + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "name": "spring.datasource.dynamic.hikari.password", + "type": "java.lang.String" + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "name": "spring.datasource.dynamic.hikari.pool-name", + "type": "java.lang.String" + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "name": "spring.datasource.dynamic.hikari.read-only", + "type": "java.lang.Boolean" + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "name": "spring.datasource.dynamic.hikari.register-mbeans", + "type": "java.lang.Boolean" + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "name": "spring.datasource.dynamic.hikari.scheduled-executor", + "type": "java.util.concurrent.ScheduledExecutorService" + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "deprecated": true, + "name": "spring.datasource.dynamic.hikari.scheduled-executor-service", + "type": "java.util.concurrent.ScheduledThreadPoolExecutor", + "deprecation": {} + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "name": "spring.datasource.dynamic.hikari.schema", + "type": "java.lang.String" + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "name": "spring.datasource.dynamic.hikari.transaction-isolation", + "type": "java.lang.String" + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "name": "spring.datasource.dynamic.hikari.username", + "type": "java.lang.String" + }, + { + "sourceType": "com.zaxxer.hikari.HikariConfig", + "name": "spring.datasource.dynamic.hikari.validation-timeout", + "type": "java.lang.Long" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties", + "name": "spring.datasource.dynamic.order", + "description": "aop切面顺序,默认优先级最高", + "type": "java.lang.Integer" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties", + "defaultValue": false, + "name": "spring.datasource.dynamic.p6spy", + "description": "是否使用p6spy输出,默认不输出", + "type": "java.lang.Boolean" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties", + "defaultValue": "master", + "name": "spring.datasource.dynamic.primary", + "description": "必须设置默认的库,默认master", + "type": "java.lang.String" + }, + { + "sourceType": "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties", + "name": "spring.datasource.dynamic.strategy", + "description": "多数据源选择算法clazz,默认负载均衡算法", + "type": "java.lang.Class" + } + ] +} \ No newline at end of file diff --git a/target/classes/META-INF/spring.factories b/target/classes/META-INF/spring.factories new file mode 100644 index 0000000..6d4b5af --- /dev/null +++ b/target/classes/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration \ No newline at end of file diff --git a/target/classes/com/baomidou/dynamic/datasource/AbstractRoutingDataSource.class b/target/classes/com/baomidou/dynamic/datasource/AbstractRoutingDataSource.class new file mode 100644 index 0000000000000000000000000000000000000000..689ecdde1ab7e68f826c037fc5519a298f396ab0 GIT binary patch literal 1539 zcmbVL-BQy)7(LsD7DD)^EsFA^qHRGU@*_}WbWmn=s*bjUGu&;{Py=mJlT_eQ@CCf_ z0&jHi!Uyo79KYQ}8Z6#8o!M-^J$t^hXV3op_5BBcMXamnL0&~SW_vM*yJDDEq2r#I zEr{WM5{pSJF(fuD+wwOV;@R8|gRU4>S>9VZPSbR^9hYIW16;`MQaM5^wQuBwm`n#+ z)Wrrh$iTKG0ygo_jLZwKHK^#-FofYGRy2%YR1BFU9%xv_nuc*)({Kwj3~T?LMuru~ zt?6D#5lqnx!QUfNW2&2%>wB*W!T+b1wA9WR6vvX%q;vH;wzyU>9yneakwomOJ>P0dn^t`j=?sqA%zL`=3)qy_zwewln* zA-`5-%XF~uyk!d4F+nXSWmC~wRHmphP4hUI8qsirN-^A|Mao*Jr4ecwms&nzFjSI^ z;RH$L6P^AuZ3`5Mn*$z3l#HJ%8ShXcDz`%=3B*(JM-&dpB`gob+@bZXl$HApxqMCY literal 0 HcmV?d00001 diff --git a/target/classes/com/baomidou/dynamic/datasource/DynamicDataSourceClassResolver.class b/target/classes/com/baomidou/dynamic/datasource/DynamicDataSourceClassResolver.class new file mode 100644 index 0000000000000000000000000000000000000000..aada843f1b2f75dcdfd6238f44a09b490de28df2 GIT binary patch literal 2518 zcmbtWTXz#x6#h;Um?Rxq8p<7^ax+ca1i4wHmP<>eniixL6-7OnoTgJIGjTGh^q08! z1-#-?YW2b8lMntRmrMN4nY2k;mgwST=A3=s``dfZ{PFiMzXG^`)hrI-dItM(BaNF` z$64IRCl)@< z;xjzZ`k!0)LLlh{HGy0?2y3OLw|H^6R1Ru26aS@Bd=Ks*o_sLMt}g)@Gn z!bRz*X;*n<9WF1+6ZOVd>TWe?m8z@0th-LBDkIqpTA`y#Q!#0ZlzBrjUhk=9;H{XdWE)}d zc-2q^`nxQ&}cvCv$(ak z)NID{R84v}97i>qn;j!yEe7G7tP{>N`L5=ik3yF1>dTsWFDGM4Ii3tvwY`f*U!l5v zKOO9Zx`oB5Y^o@Z(KQ+v^ZquBMB)4^UDPrv*y~OFDvm|ayzv`(_;7Yf2JDTnQH>$Hk zl_4c*QsC?>ENrHjp?JKk1deUVal=W>rR76A;n~par%pe#h5k||Or3qL^=lFyM4 z+dsq$0w&gOQJc)&RJ_nKS!qUBQmKqWD6-OJVknTWDWNBG}VKC3}6?7 zI7(-`a0>6@JyKIRNhj0f0%y%FgSSW@BBex)0yRdcQ>4}?!#hXK^E_R^C0wM9cWC7j zCU6-yafLikBSoDoPU8$yoZ#&=Ehls;w4CR=2V=BfV4}mk*?h;85uQd#8O5#_be*tJ zv>1f2aP9>%NXB>G4Czncp@p|oR65UalDca6j*gOikFgMmCnya6j6DLn3cc&-dx8O7 zppkX#T|@tG$UTLnSxU>p_`>}487TsP?rlHW^|E;4PM)L1(y zeY(tY#3}S4`4@&PjPXaS#7J2i(4b(&uJV+i;WQ1m!Di@!R3MYvzm5Yx;NbVX@Nb3B O-G+67&k5cVxb_dmx|Xp3 literal 0 HcmV?d00001 diff --git a/target/classes/com/baomidou/dynamic/datasource/DynamicDataSourceCreator.class b/target/classes/com/baomidou/dynamic/datasource/DynamicDataSourceCreator.class new file mode 100644 index 0000000000000000000000000000000000000000..020feb1a589dc5fd37e29b8d6d6bdb87b30e7635 GIT binary patch literal 7568 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 LLehj6;pqPZBz{9j literal 0 HcmV?d00001 diff --git a/target/classes/com/baomidou/dynamic/datasource/DynamicGroupDataSource.class b/target/classes/com/baomidou/dynamic/datasource/DynamicGroupDataSource.class new file mode 100644 index 0000000000000000000000000000000000000000..8a421046878993a170e9b5bb1b2a062b90d766a7 GIT binary patch literal 3872 zcmb_eZBr9h6n<{9yII0QA_5wv6)UxnfU$~J6R=hhYYS+_mujhXNw%;W63mN=efj)O2yjzruD}`@K>b6`8a=R2kF^VqCnJCHc zvWYwLTQ*TKQI%vZg1Z833*}O6dB$FH1iGef*>~-9!7eVOXRBqmxR4b{Ya=_$8TB}3b+yHK>NwK9zyY`7lt_V?43JB4)J#_S1-8DnnIt)39L<_iTqdlRM) zT@lbum+~|#HsuzbncC94Q@&)+7rbbea(3a0U3TT(Kh&y=E`2<ZeOgSu=>c=vMK~7DfLFGt5fO;7FgGsJ;4%7zIM1tVOf<84)!!D zFB$$mvTfqw*}*sApj6Z;6$M?~-qsE_C#>s!U`rVJitT*-&i<^Z!H|cVS%GtwN`TzEPN*MMsdW#X-OT$2NsUuxP^-n zc1Pm_a;oMQ@-pRnn*$E67s0rN@9;gFX1Kp)1U;S~SU=>c2XdVG7_x8;M>xys2B@m@ zW|C}?V8ANOT9|XxOQM{80;jeL$ks0z-=@Ys%C;PshP}M(6luXov*DrMxWIGe$B~cP z2L83CYsrtj{6(;vzcIr6VZp*66_T~#eOWCKc*mpnAx3%t?{eMGlSN8|`Q4R#3Xyz{ z(6y%sKY^A^J%OGaegflHPwqXgz0kQHg2n=i@z+c@3nYOf802ZcLctJ{+!4>SLU@$4 zBSd{E4EtIqxDWBpO8y2d^$d{^uGaO)A4g!dwd1&l-K%)lu`P_?0FeaJEfLKoh+{+? z-vaR<-uG=hMy=BRPTFWlJD;H~gkN|Pf{cdY-JbODEXCHx$Em;2{uA`@!+j)6gXq1NJUWQ9GWI&Vum_Xup;Ka=#1d}OY}x?fq$lWiJgjU3zCH9v!kNO zA{0~)=rVkbJrhjRTG^A;vrML&kCcKD2MA&i=5jncrj2_c?~7*_8E`Y6dW6WxDz@E} z=dp|?FA|!6W6CtBt9VBDu9yK&$V}+oRbTU6&+i}j;@`Gj!Qa0dKJ4G$A!w6YM#;c LL3N$zA$;*aWid%+ literal 0 HcmV?d00001 diff --git a/target/classes/com/baomidou/dynamic/datasource/DynamicRoutingDataSource.class b/target/classes/com/baomidou/dynamic/datasource/DynamicRoutingDataSource.class new file mode 100644 index 0000000000000000000000000000000000000000..92c328882c2feaea9eae79ed03c97fd78894b84a GIT binary patch literal 6398 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/baomidou/dynamic/datasource/aop/DynamicDataSourceAnnotationAdvisor.class b/target/classes/com/baomidou/dynamic/datasource/aop/DynamicDataSourceAnnotationAdvisor.class new file mode 100644 index 0000000000000000000000000000000000000000..288f5f119b1948499a813d954bb892fb3995ea30 GIT binary patch literal 2327 zcmbtWYi}Dx6g}fd*0G%jE^PvoG%Y1KcG{&BTEHRTI0;R$(~6*qRN}*Uy-AkXJJ#;H zr9Xv#0TLxZBp#oT_)(NI>y2$9r*4%Go|(OK=RVFocgFwx``hmTZsJ}VIlOP+gB0e{ zh~d^aUcv1&hY$7YBLg32kV478vVl+1NMl70pBnfqjY*XCuxg-UU`=35HXb>aEpV}_ zyggH@wrn;X=~}j4o8@q~B#>$=#|^AbAaEtr^xK}}?(KTAWj|KlL8yH{(xLv?vg10z ziom^m)lx09E>+8EsE*lq;>wm|nGG38Uv<35(OOr$M(K7aExWD?WZ)=w(+zB@wFBjq z79R*CDyqSNr>c%?Z*^LA+xtq^o0R0L%970o(sT58BuoVRjxTWMtWjtrd`2L!5T!68 zR)OkSeGX!2pvVP;_g*9ESA*>MA>WpBES&tcy8rLGAf>m$hgr>jlXs;dKY zOSxN}W|Mhj_Uz!eVziTAe3812^H7%*0&~%kQ%N)F+rg?W-3@86cuxf0%MbNYr;cy# zcD2lb6JfnGdb?-Bd`Sxe>Gkh4d&nf;zy@)wcbsOUpLyJBTLSTx9@Aa&Z8(}w?>vW; z5go37D<8=$X7D-T&}*|P-91wy96GSQo^!2rCyR4?{^h5Lh2}QunOiX}6UZn6lSFW}gZ*8Cd&l++n>6pokE`h-9QAm{Qe&CT^$5BMZ6S#ZE5C$4lAU`aOZYedT z{T>rYw!FT`&gZLr%4`SvJ`D-~KY67oeZTZj>z~_IUd;~nRij@|fro=uy4t6A@=I}p zcf{Mn8&((j7|bx_WXENH34Hw``a?VT`MfFNGM}~__z>~Mi|H=nTNu+l#5qa%3}*TL z8oxS-a|O&74k3zvW8w(0om~77i9ZU(r$`nkAA1VpN9x4!I>#Im91EO_w6lauxP~_< zxzM$Pi+Gb?w1_$G3C_S2#4{d<8MtiV3I%WRX9(_I<$}mzfuoMc8^{Jcd33 zlSBe2@o&O15m*_>ezuqWEYVqFSo743L(C8)5IBMBbix}ginu|UHa2&J^iD35I7GIZ z%lv|g+7Tvqiien5`We&xRJyLq$Y7Nvu7s}VyA~qX8J7E97DI4<0yo04@b;mP7;gRt D7I%i> literal 0 HcmV?d00001 diff --git a/target/classes/com/baomidou/dynamic/datasource/aop/DynamicDataSourceAnnotationInterceptor.class b/target/classes/com/baomidou/dynamic/datasource/aop/DynamicDataSourceAnnotationInterceptor.class new file mode 100644 index 0000000000000000000000000000000000000000..0acc58d20087c1e70ffe0299be61ce257da738b0 GIT binary patch literal 3519 zcmbtXX;%|h7=A7!8A(RLfD}-vtsARjtF;OiQ5Hc1Qa}{D*dZCn$Yds+8Bn_K?O(8e zpkM7d6{S6=?dSd{d-~i-$YMg{ayZ<%bLYM9`n=D*`RCu?{{(OfKSj}o2?di;)M26l zdvPVo;c65KOvNybYjQRtXYWdpSp{=Z%;UNo7NWR;n=!<&7{x8Tr{Miv_&`oSl$vhG z@DX%5TZ$oxlmbJ+vOsKZW-M`gW_E0R@}@vMaYtX*HA}Zwv^n23?Ug}+-6M|e`MT{d z=+>Gca4@xD>lrgSs{8t!v*sp^xvXK$8lGdV8?L};BI#tbCEdxGDQ8W~OKT~Ldx5y- zQC=JUKP@!7zp9xT-SwoAOD|z0r-LRsD@=@7y5|)`8LnxiB8oB%v=KRP+NM7wP~X+P zAP^dHQp{mv!nBR)wak*?&g)ASSK0pUMbt#rht6i=_Wwg;b zZVRvHTC24j1RL3HAhbMf>wM49pn|jllO&+vj=&+$@GH<*-kF!XYI^nzttnyLxs$Q& z{v3N5Q~3s!hN3x!88>B*^j5@I72+o(iK#e@BLYLUxTr#9Sp%zh4X>+s6Q>lcs<4nz zV5@MDRq-*7sBqz_@UbS)U(1qST6ES<#X9b)xQ7h|pQ!j0pQ#wc=Pag*FYu+nm6{r# zx$6bQVHIEDYZb5Jkb-Yie2ec?97Lyr?^XPO9|f9<s!Ek@Nz2gPstz=)7=Dg#1rC($F3%XL zBIfHZrSs{!@WKqf%~;K*S8Spg*YFJ6mm!p7Bq!Q^i;PXnMQMT4Rm4g*mLR^ki!q#% zdG)fAOP5_eW88JzRW0ebhE{dQb>Fl|t9zDBJ5|&pK#q9|KZ6wurIo0vobHs@t8hdp zrZ<+!`oss1ZU!IaCCK%?8>XM$0WuUCt_9Ye33i)0!&!O95tx0h+LlDVjo`{9-kVAxz|DPvBT-33j$i7TwFctw1|M=-}Ue&JXa?TZcIR$wI-L)D%3)xqOBxLk{P7 zly5_*=L~4-c?8k(2kI6dq5dI4zXm&wag9#VTN{Q=&_E}TUp2I%)$ejNxbwFZ_u z@|LIx+QSb5FJwH&(7^Emtq-FK7wP*Y`Z$7f7{yhLVG-jnFcHKzL8(RN^EB%g zk=_;}P3I$3pyMr^VN^}H$t;~^Tqil6V}?Q;HJ)tynZfhiE$fn#dWTXnM{THof};wC z6kOo%pW!Ds!%?AE#jFv!^e%^m0oswZj`Z|CL~9WN%r#g0N@@}*cp3>%#&eMIjaAdr T^0Xdp2R#y{QqMST)#37g7sleE literal 0 HcmV?d00001 diff --git a/target/classes/com/baomidou/dynamic/datasource/provider/AbstractJdbcDataSourceProvider.class b/target/classes/com/baomidou/dynamic/datasource/provider/AbstractJdbcDataSourceProvider.class new file mode 100644 index 0000000000000000000000000000000000000000..3df7d26e13cd8144222bca9638ce42d6e8d951f9 GIT binary patch literal 4513 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 BeCPlG literal 0 HcmV?d00001 diff --git a/target/classes/com/baomidou/dynamic/datasource/spel/DefaultDynamicDataSourceSpelResolver.class b/target/classes/com/baomidou/dynamic/datasource/spel/DefaultDynamicDataSourceSpelResolver.class new file mode 100644 index 0000000000000000000000000000000000000000..c300d802eff8f35a7ccda134cf1486dba7388ac7 GIT binary patch literal 633 zcmb_ZJxc>Y5Pfq=JQJhQs3|N28%Y%0La>QgXb{9h!ASbOyESgO54n$k|4J*t!XMy| z5@+uMDFTwh=DnMF^JZsmzJETy0Gy)JKm~0Z+ctJ=>=IVab)>TkLZ#aq5vo_QuLvzi zN9ryQU6njaH{eX$i9H#NWTH(xOIEXIof2*xFAjw(<52r?F8tR>hT0Rp%w!tpiKj&R zq5?5cPcjd(K`9Sd9Tw`4#Rrwf!Bi!Ef}ND#gu|{gky9xG8I8p-OLR2u_ZCxxy6HcX zL0$~H;2|{1m#(#0rMJu=C#FjiHLMVB{%t+N=6t$)ccQ$EaJgLjzkVAIVDVx3etcz% zndfG-ITFmoTs!&zy%+K-qmh9<)ES#)0UB82h&WznH0CpAi`hDUGl;o!zi@U+b>=h` N8)&hI_blqSegT|)rCI<0 literal 0 HcmV?d00001 diff --git a/target/classes/com/baomidou/dynamic/datasource/spel/DynamicDataSourceSpelParser.class b/target/classes/com/baomidou/dynamic/datasource/spel/DynamicDataSourceSpelParser.class new file mode 100644 index 0000000000000000000000000000000000000000..0d5ca2a061722baf4f8a203c5ed12ede74c5bcf8 GIT binary patch literal 273 zcmaKnJ#GR)427ThTaqeANSQQoK}kb_B1HlPu_rLIqs^e%@oIKJ;%XEeAcu;W4FyG{ zG?w+`_r2%upYsL43=aj00#m}cmDpLr_GP89X3cM^29-&1H%%)brQB65+AbLngdCpA(*!ryz|pOG$Fm%hvi$A0OX(9${g$;hkvX uDm`#@yXPt!x?d1?*}?yC2v7HBpS!&^X+j_j^3wo~2t$ne4dXlqm|OwWSyZ|J literal 0 HcmV?d00001 diff --git a/target/classes/com/baomidou/dynamic/datasource/spel/DynamicDataSourceSpelResolver.class b/target/classes/com/baomidou/dynamic/datasource/spel/DynamicDataSourceSpelResolver.class new file mode 100644 index 0000000000000000000000000000000000000000..7e605cb05fe3947729edea3025bdb7d406ad9ed7 GIT binary patch literal 235 zcmX^0Z`VEs1_omWPId-%b_Nbc2KJ)V;{2SlR7M6h4WF#UvPAuy#JqI<;F6-uymV_# zOd&=FuHgLAqU2P!%p9ODC6~&)#N5nem&B4ph8YEBTu&>}sMiHr(^b literal 0 HcmV?d00001 diff --git a/target/classes/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/DataSourceProperty.class b/target/classes/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/DataSourceProperty.class new file mode 100644 index 0000000000000000000000000000000000000000..6a1af3de465273daaa32dab7509b448adaf743fb GIT binary patch literal 6380 zcmb_fTXS1i75iAN%1SmaD+NN=uOJm361}(SBmSQWGB}bPu z34sFT4xuf#mO{%tff8s7jRTZn;DIv36AyiY7x)3ZFw8K_P{OzNK9Y{5-iQU^{*p!w4>z_)-ilxDZ7Lo-pxb3}M}QN=Cjc%~z!Psx)7d z=IhctEzL6~z7gZ^v(kK1!hK87bJ9F7&9|lbjx^tu=6lk7Uz#5X`G+Ph#;_R|EL@V` zA6fXZ{Jvn}Cl)SS_^E}TS$NUHOBPIxO{x&d2??}Y4Kg9; za3x>PE@qBrOWB!RW>-F6D5uKVLcU~R|G`3W&Wm@V$mE#Sf)31}<;p2;!LJ^G{sb9*9x<#+;N7LzBd>u&u5wY)D5gteb0O!5TDDG zr)z@g8QSLC_L^i&l8!3Xbgv*(bu-#LdOfuy7;d6bFcA(O6U|6Q$7-@s`PGyoy#qDz zSSpUv&mm^8z6y#mxZXemH+#{qyS?#}eTXXhQuS^9c2U zn3t$Jo#NH>bWfS|9bv%MwraQSNI5mTFqv9XwZ%t-zSCz@`8~4gygcVqrFngI@E+OE zMV&spe{v63oj6j=lq$J0Z*t#ah6nZ9bq+SIPiRoD&0zi0=~?^A=wSVWBJ@`2n}gL{ zt#UzM5e8!VG4McEF0lUQ!|HZMOZR{@4@z@Tnn`J!2hKAW4T8mbo4P8(OSY``2HkX={Zt1QRSfricBNwtg)aQg}3 z1l}R3dsPYOUR4C_SrUgbAdHR$4k?#TIJ&=zMV(-dXMtS&YdP|k_H6Br`#A<;>c$H#A0eT`!>_=4iJ%4dv6b9_+kDR9nlq(FmgMDk zjy5`v1UT#E+Q;COD%-{&vBn_DfFOoQJis~L=)679nWYinY&SaJ73j>83UF>~bRG|M zW+?_Zw>LW98|cgu4sZ@NI!^>Tv-AU;%|_?_fzF3<#B(0!zPE7S&Eb(Nu!bAmw<`BO zP4VPHvML!-*-;$xgfr<66jgy*eY#sMkgmDo%s>m;WEI}Yz;|$@1@9qxZ*Z=$HRmd2 zEV(`u94Nl#KynU9pofD4*=r6Y2Z;oFUvQwdH3yQDNCLe-I8ghV1IbY)fj)o_vXvTM zA&k(P1L?ESz=!ZiP#|;7f%E}t$W^EVwfvca=;cwjFfx1*;n3xg-=pJMgj+9<{0<$H zqr=PSR9%9usZlm=XzY)OJE7OmbqV&E6Iw>1%IJ2(q;$IxCoG)EYe-x|n-g{-K2ncs zlGNi`jwvL|BbkonBlWsbl6u{k6BSa-BSoEJyM6$=_5IA7fFtraJqz)@JL-w!bjTZc9XQx?Qyz= z)Z>x5ot`QwbeTeEuHx7AHdK!TV*#^BbCY_FVrYYbwZ zD2z#C3$jEJ%V9>arO=LnK;Kk%XkADG>on<$5!$IrVRPsBJPX0<9JvH@Y#Ey#k@0RfBnBtsSu80JZR&Bu z%EZc0PL>m{ntI)cGDT`8){FM)b4_Jpsd!#y)zt4=%4F3{EFkT*!Hp^tYsd47R!xo@ zQ>IwW#EQ~h8{N1vv6wusxH4@H7VJ5_B%WYf@h^N7k8#1m_$NL_)Iy2<10UyNw&E6w z?h`~IKGlAO$BDwcIiANSi6RuzqfseV*5Jm9^J{dnt6vN-pkIxas zMOH1XNL=GUh_W&aN(qK>jZ@Y8Ei%xHaFtNygjNoDw1|&ZXEu&{WOF6C&JQqnE>@FE z-7DDUIYxsV>%Oa+J@?ou?)3=QV|YR0s|Tsac@vTs%D!%>dbBs^F(Bo}|9Nzk;vyv{ zukrJ*Vl!X+Z%J%f##Sj-{v-x8?q0@CiJLX%iM%y&i^jcz2Q}^!e5=O&g5Rp~2En&! f>`0s;jW-J3M!bwwE@J%(JMWc>fiI|&=!^dcJ9=n+ literal 0 HcmV?d00001 diff --git a/target/classes/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/DynamicDataSourceAutoConfiguration.class b/target/classes/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/DynamicDataSourceAutoConfiguration.class new file mode 100644 index 0000000000000000000000000000000000000000..ee1184649d25c3bf4fe4020ba1042a80885a60d1 GIT binary patch literal 6116 zcmb_g>wgqi8Ga@t%rY4)4Nxv^ArxCeZlg$JC5Yw*{8HILysr5aXp-}>={kZPte*Z_Oxjm5yGzCZ^q5k0cu#cdqhHI zhZQj-+|z5ujsEoHm|;JzQ<|fCSdWe9wkiI5ot3GRCdEJb|5RF#>+o#i=qGgd?6}A@ zQ)a}lC7h`XleI?ridMlmUdfuY#`oeCXY5rw7Q7MU`W7K8jB!S+ig3|eJr3K?eapl&Cb zsRHe=Ji?HXv|>}l2#yHdO88m$G@@VbFVfo$lLA)>?JHpG=_}#Ek|J`-loJ|dm_$Og z$py*Oo?uFm&>P4+z(hR-G@41)b1`serNCY^i8WEi(q3NmynVY@1e5av`God|W8OBT zxUtY}icM#iFP?q7L$gQ;5?V^8z7naD3D4PXb6d+@a%OHe^Mkj)Yh7`%`z^v4%zvdK zRa1H_P2&4Y(kYRNi}f+ujd9C12-8#U^oht=xF{7CqvaH2p(1LQCAC3Lx1vp?XEI^g zDM~j218w;lCSs?}h}J2dKHn`eTdRsk(ITNMs9wrv=h4ZS50b`rHenT?#U6opiN#P5 zq7TK}tz}$R@iKm@;%B&`;uXBA;x)WpKB6!6tN1zIQ1J`Asp6OTm5R6Uwu)clH!7~8 zMa6INj*Q=__&xq0VF!6PCt8w;nPwv%W&PDGnu9HcokYgFD&E7a3Jsr;P@BDQb>Ym} zh0C)GFTJ~P^I}u>+_g{6ULwTI!t)oi7v7NZzKRd-Er(-E!Y!z>6Ju61a17 zjFDTe5-ycf(3GtJalH&3T;=#k7ohs;wv1hQ`R^mtaEo;Galq|>drU-Q&9 zlA6n^_=AL1wK`G0ROe}A1h~kk)y=qHdL|&ter>2X#Iy+)i8(3S#}hIv^ya8xdS~A zPQd|uiC}`6xOn_B-);P{l0iHF3;3t`7Vx$6s2ZKa>fQz+BLg{ukk?$3fznWrSVKa< zA?nzJ^*D@eI8r3N){`E>=4GXKpwpB7FRXIVj?bf-($&63SNhrvKIJv<$zWY$Lk9OY zW>DjPU7x{*E7(=D@jC81_5n84Y`%dl6`04?(Z)I4Kie<*9=w5V6=NI3$rgO;hFWdM5PKLlANx`jish=*NI(k4r?Z;@swWY^ObH`0EgS3hpKYREV^F#BfBjeqLIB~-};mCEv zJLJjr;|J36~H&q5+h#twUy&VoaU`e~s@aIqInskira@r}J$cWBGRYzkH*6*8l(j literal 0 HcmV?d00001 diff --git a/target/classes/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/DynamicDataSourceProperties.class b/target/classes/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/DynamicDataSourceProperties.class new file mode 100644 index 0000000000000000000000000000000000000000..3198381ca24221941b9b32e7376eb6b2fe4bba40 GIT binary patch literal 3932 zcmcguYj+b>6y1}wNtzCnhEha%79VY@9Tmh16`>EL5NM@PUcPRVX*z8(6K5va`pK{U z8(qsRSh|)!z#rvu-?=l%4qy5ovN`WoT^oF>PBhD7FDZgv@94NouTzL$MFqO_no3+uUloJ8}oEyC2(We zb!w*T?_9Y``x(!7g>RPW{&39RTt#>uw;y$`%}nm^Lx^b1EB{)S8I-_QV7cFMOQx&g zXw1Z-O;=2phWl%myc(T~@6I|-#T2&OExC28q~Xr~&g>eH0K%A;KyyKqh+m0`M%dtHTiv`CmhXG!9Mb-S#ako^Y8D-zx_Kli~tYvz}UDNZ;5V+1$ z6Qdb-4XF)lOSqPXv%vv=65HFRYp6xzX0X@vDK%r+mVZ@4a(rS{L*JZJ3YcWs=G}UA z&2;Yzx+>Y{6h&o~uyP(Q_W2tYeSi2r;?$MJ@EklPBbF_j{&G|RBjXdXWHw9S5M?9@ zqAD1U#A0>eb*{Y@_7Bpth?L}7%wQ3*WhUVU{%kf8%hst%5|*=bdR6j@fq&@R#{ z2PqN|S;G=hUcO26s)+%DD@tk@3(@x^Y}SiW;@EgMBP5LvQ7F!dTqxJ1gcKTWr}R!O zqP5>4{Tj$3{TZawExL@vkEYlng`RdTzbd*&^bB1H2{Rfu6!9gJ2^rHS6A>no2`RHY z{z@4m(Ff>d#F_I7ehjjXzxZdkIFo!z77OvyV|!@_^+#;TdN_hHq)QZ?6Hx3<~( z)zvvwYBs8=@P5^i$0U1pRj{SII*#KF4abA4K45FXvbW6AP2p|uO4o4$Z}MW=w+5q2 zvfzl)tYF6}nq8-kxTfP2PBSdME}){r%ROL($;Zl;jtZ(e&f;7ewwyRR#&BN4?R`Bu z_)V$fZM>u5w1nI}uR6vtq2a;H8}157>*~>Pr1jWZTH7>>K3_1crLZ3OJbNZ8b#rN* z-DG?*emzAM)20@dh!lPj7mi8?=lCcb*{!FxY^ePrksR1`bnVh!RH{80Ij?;n4<0ehUnK!0#9`{7&*g{2?E|7kRX49%F{t zy+)(+_!NtnqJ*wY!=M~E{s%vFApWNMK@$b}i literal 0 HcmV?d00001 diff --git a/target/classes/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/druid/DruidConfig.class b/target/classes/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/druid/DruidConfig.class new file mode 100644 index 0000000000000000000000000000000000000000..b9fcfabed14d5b4d8d9455d0b473e786cf9132e6 GIT binary patch literal 27451 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

GC)3lu4fL#U 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/druid/DruidDynamicDataSourceConfiguration.class b/target/classes/com/baomidou/dynamic/datasource/spring/boot/autoconfigure/druid/DruidDynamicDataSourceConfiguration.class new file mode 100644 index 0000000000000000000000000000000000000000..ea68c6172a47ec297d0b83c2ab05ff44f92cfe3a GIT binary patch literal 1261 zcmcgs%We}f6g_V9fDq_|XMqj77NL4ki46#;N{WO!YEe^#1uKuo4K6eG$m2=jvsfXq z-~;$5#GOZ@CYnl+1vAq1^|kNuxyO%RzkmD$u!pBTtYWK+$6Y*O=)BfO`!@`$+dE^1 z*1$%JVJ*-`9py==+^Gm-ifjf}ig+xXF8Xz_(p^mO5=FJhawxk$WqxIn$HeDIU38M|@b=VU;(ev{9KhsAmbUt$D@JtHN(}L1_Cg za(2(fR4{Cvy|ko-7IbL4}1%bZotHi=c-~f;OS`(L$R+{)gMhuwL9P z9t*nG$Kkn>9?!5sFH7+;`iTnJ4)u812iniSgMFjG3c6%F6b4@42HAe;w{a7_a{N|l l`?yW*9zorqc8%t&e*}}*z~;=qi+h#dlDIGNK;ogqqd!SUprrr+ literal 0 HcmV?d00001 diff --git a/target/classes/com/baomidou/dynamic/datasource/strategy/DynamicDataSourceStrategy.class b/target/classes/com/baomidou/dynamic/datasource/strategy/DynamicDataSourceStrategy.class new file mode 100644 index 0000000000000000000000000000000000000000..2a4fa5a1dd192013b53ced7f04237d1bb4a44dc8 GIT binary patch literal 332 zcmZ`#I|{;35S;ZFqm5v1t&Lk)Mnpuc1dE(tlRUzcd@y+ty_$sw@KEA23W8|2v&_uS z?CpNJ0+^xiz;)0g3?pS!k?KsX#fYC*MW_hl@pdmq;mYWQx4Jamq+Xo3JS6;KdIZ)a`x#mRpJRyOtXUZW2WzkQSF=&v&h6y0ro+IwEQ? z6=ICZ(_#|%5R8}=-R zlYkc{me&uslJdCZahOD|uwof;QtVrH$h}S8<$)`tJ!`m8`actXvJ^3)(}YqB_*EGQi9W?aSV%p-=}}@=T4&<>;IP6Ady1b?*v>X z5xvi|vExq7xtQhq!dLHI9p|N1Ri#Z#VVVNQuDS|XHE|Y`CM?`FaL>ejwXd0YfQKd? z;W5L!;v0QghVB1aYAQ{dHZ?@Y+Hv-UD;YM&mN>PAsF5oz)z5{Fkj=nrdS2#gF-#5> zx4GP-&gTyGNajwbMzt2fWB(l$g64vPG~!`cAkwUrP9)sG5)Ehx3?_X!^{zC;IkKrP zw9e4!DRi=cnc5Lp?Hh8hwIgVspnpiAv$U!K2QCwE8FOSQG77*v7HFr2#W_MKG6!0U zE!S%wq3fS9@df$%F${)-W1Pw12MX^Ar$O=NQ=XNSb1{n>Z~|qV#|8R-k#L!w@?0WM OnLIhlhT%$TUHuIWQa338 literal 0 HcmV?d00001 diff --git a/target/classes/com/baomidou/dynamic/datasource/strategy/RandomDynamicDataSourceStrategy.class b/target/classes/com/baomidou/dynamic/datasource/strategy/RandomDynamicDataSourceStrategy.class new file mode 100644 index 0000000000000000000000000000000000000000..e4b69633e3d033846a6c6142145f215391addf73 GIT binary patch literal 1078 zcmb_bO>Yx15Pi<3SxA<)^do#02$Zr(phn`DszOj9f+8xBswXbau9dpl57_mhhF=92 zpd1iqeiUN78?+En=z)tp^E~s$elz~|{qq+94{*PZGq_U6Rjf5|4cCoz!&ogFH*MUq zv2NowLv2e%N^dh*ZhN1h`Z)F_!@@vC@_81HWU?nl0U=9+*b~9NNR;uXU{xQel;PRH zi$gvVaj5(_`=fk4;W6*dko9I)G`UF zetFE`x`RXUM(|9lfDcrvd+i)J;_2&vmytx!Fm(%By4&Jx9z*l@r%Yb`e9ml*tv{F8 zrXpvDY8(ljB_z$mQ|Fy{=X!D9C(1~@{tuBhjS>#(XxP|ra0jl9wu2734({TfgH3RT z6%#cp55v>{)hd!U??@n`G2a~>N>4L9IyL)=BfzjxcG`<0FG~^`X}))mNa5$LEA(No zr)LZuw~)4&kH2M6@8(N>Gf6b`sd`Jg(+}tqX(W$yKQge}H-I&kYx15PeQIA1r~ip@DLys*)b+(jJRAG$9Hog#%Hf9v!chxN&T;>#f>JQ*YA;z0X;nY(s+2fgc@6C+;=kKpa0B3j`qk_E}+A&JOzOLa7L-m8U+J9syce)dX zsOJ*J&>U&2zUIrRO2=YqDAF1^Da=HqI{52gnOj&MdU&{Z*<@neRoW=d4$^e&|f1(u4Qsq#fGRp|Z)TjlE8H zbS-WKH^R>Nx9PQ#o>1|MKG31ncTdY62R@DX$x7ogZD*It&s|c(egjq17&=9P+-t+7 zvocRpWj&`CS*YZ~IJqEBU;PN}47W8YLwhsUc%CYe6c5fY*iH$YAfo>dt3;ay1w)-~ zoGc?-IeGy5S)`v8fDk-HOlM;Sz;nDHFTzVS*W^`7F|x)0 literal 0 HcmV?d00001 diff --git a/target/classes/com/baomidou/dynamic/datasource/toolkit/DynamicDataSourceContextHolder.class b/target/classes/com/baomidou/dynamic/datasource/toolkit/DynamicDataSourceContextHolder.class new file mode 100644 index 0000000000000000000000000000000000000000..84e07427ada8812929f687e48f832320e1b8d2bd GIT binary patch literal 1682 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
yfFvwF=>?X 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