feat: release 2.4.0

This commit is contained in:
小锅盖 2018-11-18 23:00:21 +08:00
commit 10cb533d54
136 changed files with 5583 additions and 0 deletions

View File

@ -0,0 +1,29 @@
# 环境
您所使用的 JDK版本(必填):
您所使用的 SpringBoot版本(必填):
您所使用的 Starter版本(必填):
# 描述
期望值:
实际值:
# 重现步骤
- 步骤1
- 步骤2
- 步骤3

View File

@ -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:**

29
.github/ISSUE_TEMPLATE vendored Normal file
View File

@ -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

16
.github/PULL_REQUEST_TEMPLATE vendored Normal file
View File

@ -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:**

25
.gitignore vendored Normal file
View File

@ -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/

12
.travis.yml Normal file
View File

@ -0,0 +1,12 @@
language: java
jdk:
- openjdk7
- openjdk8
after_success:
- echo success
cache:
directories:
- $HOME/.m2

122
CHANGELOG.md Normal file
View File

@ -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.7springboot1.4.x。
- 重构aop解决了部分springboot版本引入插件无效的问题。
# v1.3.0
- 对Druid的paCache属性提供支持。
- 还原上一版本切面的配置方式。
- 其他一些细节的优化。
# v1.2.0
- 对Druid提供更完善的支持。
- 更改了默认的注解切面的注入方式。
- 抽象了切面选择数据源接口方便以后支持spel语法等。
# v1.1.0
- 支持Druid数据源 (support DruidDataSource)。

191
LICENSE Normal file
View File

@ -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.

482
README.md Normal file
View File

@ -0,0 +1,482 @@
<p align="center">
<img src="https://s1.ax1x.com/2018/07/31/PwPVAA.png" border="0" />
</p>
<p align="center">
<strong>一个基于springboot的快速集成多数据源的启动器</strong>
</p>
<p align="center">
<a href="https://www.travis-ci.org/baomidou/dynamic-datasource-spring-boot-starter" target="_blank">
<img src="https://www.travis-ci.org/baomidou/dynamic-datasource-spring-boot-starter.svg?branch=master" >
<a href="http://mvnrepository.com/artifact/com.baomidou/dynamic-datasource-spring-boot-starter" target="_blank">
<img src="https://maven-badges.herokuapp.com/maven-central/com.baomidou/dynamic-datasource-spring-boot-starter/badge.svg" >
</a>
<a href="http://www.apache.org/licenses/LICENSE-2.0.html" target="_blank">
<img src="http://img.shields.io/:license-apache-brightgreen.svg" >
</a>
<a>
<img src="https://img.shields.io/badge/JDK-1.7+-green.svg" >
</a>
<a>
<img src="https://img.shields.io/badge/springBoot-1.4+_1.5+_2.0+-green.svg" >
</a>
</p>
#### [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**。最新版为<img src="https://maven-badges.herokuapp.com/maven-central/com.baomidou/dynamic-datasource-spring-boot-starter/badge.svg" >
**示例项目** 可参考项目下的samples目录。
**示例项目** 可参考项目下的samples目录。
**示例项目** 可参考项目下的samples目录。
# 优势
网上关于动态数据源的切换的文档有很多核心只有两种。1是构建多套环境2是基于spring原生的 `AbstractRoutingDataSource` 切换。
如果你的数据源较少,场景不复杂,选择以上任意一种都可以。如果你需要更多特性,请试着尝试本数据源。
1. 数据源分组,适用于多种场景 纯粹多库 读写分离 一主多从 混合模式。
2. 简单集成Druid数据源监控多数据源简单集成Mybatis-Plus简化单表简单集成P6sy格式化sql简单集成Jndi数据源。
3. 自定义数据源来源。
4. 动态增减数据源。
5. 使用spel动态参数解析数据源如从sessionheader和参数中获取数据源。多租户架构神器
6. 多层数据源嵌套切换。一个业务ServiceA调用ServiceBServiceB调用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
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>${version}</version>
</dependency>
```
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<Map<String, Object>> selectAll() {
return jdbcTemplate.queryForList("select * from user");
}
@Override
@DS("slave_1")
public List<Map<String, Object>> 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<User> selectAll();
}
```
---
大部分用户看到这里就可以了,赶紧体验一下吧! 如果需要更多功能请继续往下看。
---
# 第三方集成
## 集成 Druid
springBoot2.x默认使用HikariCP但在国内Druid的使用者非常庞大此项目特地对其进行了适配完成多数据源下使用Druid进行监控。
> 注意:主从可以使用不同的数据库连接池,如**master使用Druid监控从库使用HikariCP**。 如果不配置连接池type类型默认是Druid优先于HikariCP。
1. 项目引入`druid-spring-boot-starter`依赖。
```xml
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
```
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<User> {
}
@Service
@DS("slave")//再次建议不要同时注解在service和mapper上。
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
```
## 集成 P6sy
p6sy大部分人最常用的功能就是格式化你的sql语句。
```mysql
# 如在使用mybatis的过程中原生输出的语句是带?号的。在需要复制到其他地方执行看效果的时候很不方便。
select * from user where age>?
# 在使用了p6sy后其会帮你格式化成真正的执行语句。
select * from user where age>6
```
1. 引入相关jar包。
```xml
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>3.8.0</version>
</dependency>
```
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<String, DataSource> 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` 没有对解析到的参数进行处理直接返回。
# 苞米豆开源项目
<https://gitee.com/baomidou/mybatis-plus> mybatis单表快速开发3.0已支持lamdba写法 。
<https://gitee.com/baomidou/kisso> 基于cookie的SSO中间件。
<https://gitee.com/baomidou/dynamic-datasource-spring-boot-starter> 多数据源,动态数据源,主从分离 快速启动器。
<https://gitee.com/baomidou/kaptcha-spring-boot-starter> 谷歌验证码 快速启动器。
<https://gitee.com/baomidou/lock4j-spring-boot-starter> 支持RedisTemplate、Redisson、Zookeeper 简单可靠的分布式锁 快速启动器。
# 常见问题
1. 多个库的事物如何处理?
**不能 不能 不能**,一个业务操作涉及多个库不要加事物。
2. 是否支持JPA
不完全支持受限于JPA底层你只能在一个controller下切换第一个库第二个库不能切换。如有解决办法请联系作者

14
license.txt Normal file
View File

@ -0,0 +1,14 @@
Copyright © ${project.inceptionYear} organization baomidou
<pre>
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.
<pre/>

210
pom.xml Normal file
View File

@ -0,0 +1,210 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.1.RELEASE</version>
<relativePath/>
</parent>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>2.4.0</version>
<packaging>jar</packaging>
<name>dynamic-datasource-spring-boot-starter</name>
<description>dynamic datasource with annotation</description>
<inceptionYear>2018</inceptionYear>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0</url>
</license>
</licenses>
<organization>
<name>baomidou</name>
<url>https://gitee.com/baomidou</url>
</organization>
<developers>
<developer>
<name>TaoYu</name>
<email>tracy5546@gmail.com</email>
</developer>
<developer>
<name>KanYuXia</name>
<email>kanyuxia@outlook.com</email>
</developer>
</developers>
<scm>
<url>https://github.com/baomidou/dynamic-datasource-spring-boot-starter</url>
<connection>scm:git:https://github.com/baomidou/dynamic-datasource-spring-boot-starter.git</connection>
<developerConnection>scm:git:https://github.com/baomidou/dynamic-datasource-spring-boot-starter.git
</developerConnection>
<tag>HEAD</tag>
</scm>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.7</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>2.7.0</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>3.8.0</version>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>com.mycila</groupId>
<artifactId>license-maven-plugin</artifactId>
<version>3.0</version>
<configuration>
<header>${project.basedir}/license.txt</header>
<includes>
<include>src/main/java/**</include>
</includes>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>release</id>
<distributionManagement>
<snapshotRepository>
<id>ossrh</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</snapshotRepository>
<repository>
<id>ossrh</id>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
</distributionManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<version>1.6.8</version>
<extensions>true</extensions>
<configuration>
<serverId>ossrh</serverId>
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
<autoReleaseAfterClose>true</autoReleaseAfterClose>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

25
samples/.gitignore vendored Normal file
View File

@ -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/

13
samples/README.md Normal file
View File

@ -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 嵌套切换示范

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>dynamic-datsource-samples</artifactId>
<groupId>com.baomidou</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dynamic-druid-mybatis-sample</artifactId>
<dependencies>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>3.8.0</version>
</dependency>
</dependencies>
</project>

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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<User> selectUsers(@Param("age") Integer age);
}

View File

@ -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();
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -0,0 +1 @@
appender=com.p6spy.engine.spy.appender.Slf4JLogger

View File

@ -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();
}
}

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>dynamic-datsource-samples</artifactId>
<groupId>com.baomidou</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dynamic-jdbc-template-sample</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -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");
}
}

View File

@ -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

View File

@ -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();
}
}

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>dynamic-datsource-samples</artifactId>
<groupId>com.baomidou</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dynamic-mybatis-sample</artifactId>
<dependencies>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
</dependencies>
</project>

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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<User> selectUsers();
}

View File

@ -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();
}

View File

@ -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();
}
}

View File

@ -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

View File

@ -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();
}
}

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>dynamic-datsource-samples</artifactId>
<groupId>com.baomidou</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dynamic-mybatisplus2-sample</artifactId>
<dependencies>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>2.3.1</version>
</dependency>
</dependencies>
</project>

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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<User> {
}

View File

@ -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<User> {
void addUser(User user);
}

View File

@ -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<UserMapper, User> implements UserService {
@Override
@DS("master")//这里必须包一层不能调用mp默认的插入因为会走到从库去
public void addUser(User user) {
baseMapper.insert(user);
}
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>dynamic-datsource-samples</artifactId>
<groupId>com.baomidou</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dynamic-mybatisplus3-sample</artifactId>
<dependencies>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
</dependencies>
</project>

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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<User> {
}

View File

@ -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<User> {
void addUser(User user);
}

View File

@ -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<UserMapper, User> implements UserService {
@Override
@DS("master")//这里必须包一层不能调用mp默认的插入因为会走到从库去
public void addUser(User user) {
baseMapper.insert(user);
}
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>dynamic-datsource-samples</artifactId>
<groupId>com.baomidou</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dynamic-nest-sample</artifactId>
<dependencies>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
</dependencies>
</project>

View File

@ -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);
}
}

View File

@ -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<ApplicationStartedEvent> {
@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();
}
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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<Student> selectStudents();
}

View File

@ -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<Teacher> selectTeachers();
}

View File

@ -0,0 +1,12 @@
package com.baomidou.samples.nest.service;
public interface SchoolService {
void selectTeachersAndStudents();
void selectTeachersInnerStudents();
void addTeacherAndStudent();
void addTeacherAndStudentWithTx();
}

View File

@ -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<Student> selectStudents();
}

View File

@ -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<Teacher> selectTeachers();
void selectTeachersInnerStudents();
}

View File

@ -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);
}
}

View File

@ -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<Student> selectStudents() {
return studentMapper.selectStudents();
}
}

View File

@ -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<Teacher> selectTeachers() {
return teacherMapper.selectTeachers();
}
@Override
public void selectTeachersInnerStudents() {
teacherMapper.selectTeachers();
studentService.selectStudents();
}
}

View File

@ -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

View File

@ -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();
}
}

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>dynamic-datsource-samples</artifactId>
<groupId>com.baomidou</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dynamic-spel-sample</artifactId>
<dependencies>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
</dependencies>
</project>

View File

@ -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);
}
}

View File

@ -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<User> usersFromSession(HttpServletRequest request) {
request.getSession().setAttribute("tenantName", "tenant1");
return userService.selectSpelBySession();
}
@GetMapping("/users/header")
public String usersFromHeader() {
userService.selectSpelByHeader();
return "success";
}
}

View File

@ -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;
}
}

View File

@ -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<User> selectUsers();
}

View File

@ -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);
}

View File

@ -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();
}
}

View File

@ -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

View File

@ -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);
}
}

71
samples/pom.xml Normal file
View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.RELEASE</version>
<relativePath/>
</parent>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datsource-samples</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<modules>
<module>dynamic-jdbc-template-sample</module>
<module>dynamic-mybatis-sample</module>
<module>dynamic-mybatisplus2-sample</module>
<module>dynamic-mybatisplus3-sample</module>
<module>dynamic-druid-mybatis-sample</module>
<module>dynamic-spel-sample</module>
<module>dynamic-nest-sample</module>
</modules>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>2.3.7</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.197</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,63 @@
/**
* Copyright © 2018 organization baomidou
* <pre>
* 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.
* <pre/>
*/
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> T unwrap(Class<T> 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));
}
}

View File

@ -0,0 +1,66 @@
/**
* Copyright © 2018 organization baomidou
* <pre>
* 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.
* <pre/>
*/
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();
}
}

View File

@ -0,0 +1,238 @@
/**
* Copyright © 2018 organization baomidou
* <pre>
* 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.
* <pre/>
*/
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<? extends DataSource> 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);
}
}

View File

@ -0,0 +1,70 @@
/**
* Copyright © 2018 organization baomidou
* <pre>
* 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.
* <pre/>
*/
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<DataSource> 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();
}
}

View File

@ -0,0 +1,167 @@
/**
* Copyright © 2018 organization baomidou
* <pre>
* 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.
* <pre/>
*/
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<? extends DynamicDataSourceStrategy> strategy;
@Setter
protected String primary;
/**
* 所有数据库
*/
private Map<String, DataSource> dataSourceMap = new LinkedHashMap<>();
/**
* 分组数据库
*/
private Map<String, DynamicGroupDataSource> 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<String, DataSource> getCurrentDataSources() {
return dataSourceMap;
}
/**
* 获取的当前所有的分组数据源
*
* @return 当前所有的分组数据源
*/
public Map<String, DynamicGroupDataSource> 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<String, DataSource> dataSources = provider.loadDataSources();
log.info("初始共加载 {} 个数据源", dataSources.size());
//添加并分组数据源
for (Map.Entry<String, DataSource> 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默认数据库设置");
}
}
}

View File

@ -0,0 +1,39 @@
/**
* Copyright © 2018 organization baomidou
* <pre>
* 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.
* <pre/>
*/
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();
}

View File

@ -0,0 +1,69 @@
/**
* Copyright © 2018 organization baomidou
* <pre>
* 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.
* <pre/>
*/
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);
}
}

View File

@ -0,0 +1,74 @@
/**
* Copyright © 2018 organization baomidou
* <pre>
* 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.
* <pre/>
*/
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;
}
}

View File

@ -0,0 +1,109 @@
/**
* Copyright © 2018 organization baomidou
* <pre>
* 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.
* <pre/>
*/
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<String, DataSource> 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<String, DataSourceProperty> dataSourcePropertiesMap = executeStmt(stmt);
Map<String, DataSource> dataSourceMap = new HashMap<>(dataSourcePropertiesMap.size());
for (Map.Entry<String, DataSourceProperty> 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<String, DataSourceProperty> executeStmt(Statement statement) throws SQLException;
}

View File

@ -0,0 +1,39 @@
/**
* Copyright © 2018 organization baomidou
* <pre>
* 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.
* <pre/>
*/
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<String, DataSource> loadDataSources();
}

View File

@ -0,0 +1,63 @@
/**
* Copyright © 2018 organization baomidou
* <pre>
* 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.
* <pre/>
*/
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<String, DataSource> loadDataSources() {
Map<String, DataSourceProperty> dataSourcePropertiesMap = properties.getDatasource();
Map<String, DataSource> dataSourceMap = new HashMap<>(dataSourcePropertiesMap.size());
for (Map.Entry<String, DataSourceProperty> item : dataSourcePropertiesMap.entrySet()) {
String pollName = item.getKey();
DataSourceProperty dataSourceProperty = item.getValue();
dataSourceProperty.setPollName(pollName);
dataSourceMap.put(pollName, dynamicDataSourceCreator.createDataSource(dataSourceProperty));
}
return dataSourceMap;
}
}

View File

@ -0,0 +1,80 @@
/**
* Copyright © 2018 organization baomidou
* <pre>
* 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.
* <pre/>
*/
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();
}
}
}

View File

@ -0,0 +1,32 @@
/**
* Copyright © 2018 organization baomidou
* <pre>
* 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.
* <pre/>
*/
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;
}
}

View File

@ -0,0 +1,37 @@
/**
* Copyright © 2018 organization baomidou
* <pre>
* 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.
* <pre/>
*/
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);
}

View File

@ -0,0 +1,34 @@
/**
* Copyright © 2018 organization baomidou
* <pre>
* 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.
* <pre/>
*/
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);
}

View File

@ -0,0 +1,74 @@
/**
* Copyright © 2018 organization baomidou
* <pre>
* 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.
* <pre/>
*/
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 {
/**
* 连接池名称(只是一个名称标识)</br>
* 默认是配置文件上的名称
*/
private String pollName;
/**
* 连接池类型如果不设置自动查找 Druid > HikariCp
*/
private Class<? extends DataSource> 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();
}

View File

@ -0,0 +1,120 @@
/**
* Copyright © 2018 organization baomidou
* <pre>
* 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.
* <pre/>
*/
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();
}
}

View File

@ -0,0 +1,75 @@
/**
* Copyright © 2018 organization baomidou
* <pre>
* 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.
* <pre/>
*/
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<String, DataSourceProperty> datasource = new LinkedHashMap<>();
/**
* 多数据源选择算法clazz默认负载均衡算法
*/
private Class<? extends DynamicDataSourceStrategy> 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();
}

View File

@ -0,0 +1,244 @@
/**
* Copyright © 2018 organization baomidou
* <pre>
* 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.
* <pre/>
*/
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;
}
}

View File

@ -0,0 +1,46 @@
/**
* Copyright © 2018 organization baomidou
* <pre>
* 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.
* <pre/>
*/
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 {
}

Some files were not shown because too many files have changed in this diff Show More