feat: release 2.4.0
This commit is contained in:
commit
10cb533d54
29
.gitee/ISSUE_TEMPLATE.zh-CN.md
Normal file
29
.gitee/ISSUE_TEMPLATE.zh-CN.md
Normal file
@ -0,0 +1,29 @@
|
||||
# 环境
|
||||
|
||||
您所使用的 JDK版本(必填):
|
||||
|
||||
您所使用的 SpringBoot版本(必填):
|
||||
|
||||
您所使用的 Starter版本(必填):
|
||||
|
||||
# 描述
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
期望值:
|
||||
|
||||
实际值:
|
||||
|
||||
# 重现步骤
|
||||
|
||||
- 步骤1
|
||||
|
||||
|
||||
- 步骤2
|
||||
|
||||
|
||||
- 步骤3
|
||||
|
||||
|
16
.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md
Normal file
16
.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md
Normal 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
29
.github/ISSUE_TEMPLATE
vendored
Normal 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
16
.github/PULL_REQUEST_TEMPLATE
vendored
Normal 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
25
.gitignore
vendored
Normal 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
12
.travis.yml
Normal file
@ -0,0 +1,12 @@
|
||||
language: java
|
||||
|
||||
jdk:
|
||||
- openjdk7
|
||||
- openjdk8
|
||||
|
||||
after_success:
|
||||
- echo success
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.m2
|
122
CHANGELOG.md
Normal file
122
CHANGELOG.md
Normal 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.7,springboot1.4.x。
|
||||
- 重构aop,解决了部分springboot版本引入插件无效的问题。
|
||||
|
||||
# v1.3.0
|
||||
|
||||
- 对Druid的paCache属性提供支持。
|
||||
- 还原上一版本切面的配置方式。
|
||||
- 其他一些细节的优化。
|
||||
|
||||
# v1.2.0
|
||||
|
||||
- 对Druid提供更完善的支持。
|
||||
- 更改了默认的注解切面的注入方式。
|
||||
- 抽象了切面选择数据源接口,方便以后支持spel语法等。
|
||||
|
||||
# v1.1.0
|
||||
|
||||
- 支持Druid数据源 (support DruidDataSource)。
|
191
LICENSE
Normal file
191
LICENSE
Normal 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
482
README.md
Normal 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动态参数解析数据源,如从session,header和参数中获取数据源。(多租户架构神器)
|
||||
6. 多层数据源嵌套切换。(一个业务ServiceA调用ServiceB,ServiceB调用ServiceC,每个Service都是不同的数据源)
|
||||
|
||||
# 劣势
|
||||
|
||||
不能使用多数据源事物(同一个数据源下能使用事物),网上其他方案也都不能提供。
|
||||
|
||||
如果你需要使用到分布式事物,那么你的架构应该到了微服务化的时候了。
|
||||
|
||||
如果呼声强烈,项目达到800 star,作者考虑集成分布式事物。
|
||||
|
||||
PS: 如果您只是几个数据库但是有强烈的需求分布式事物,建议还是使用传统方式自己构建多套环境集成atomic这类,网上百度很多。
|
||||
|
||||
# 约定
|
||||
|
||||
1. 本框架只做 **切换数据源** 这件核心的事情,并**不限制你的具体操作**,切换了数据源可以做任何CRUD。
|
||||
2. 配置文件所有以下划线 `_` 分割的数据源 **首部** 即为组的名称,相同组名称的数据源会放在一个组下。
|
||||
3. 切换数据源即可是组名,也可是具体数据源名称,切换时默认采用负载均衡机制切换。
|
||||
4. 默认的数据源名称为 **master** ,你可以通过spring.datasource.dynamic.primary修改。
|
||||
5. 方法上的注解优先于类上注解。
|
||||
|
||||
# 建议
|
||||
|
||||
强烈建议在 **主从模式** 下遵循普遍的规则,以便他人能更轻易理解你的代码。
|
||||
|
||||
主数据库 **建议** 只执行 `INSERT` `UPDATE` `DELETE` 操作。
|
||||
|
||||
从数据库 **建议** 只执行 `SELECT` 操作。
|
||||
|
||||
# 使用方法
|
||||
|
||||
1. 引入dynamic-datasource-spring-boot-starter。
|
||||
|
||||
```xml
|
||||
<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
14
license.txt
Normal 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
210
pom.xml
Normal 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
25
samples/.gitignore
vendored
Normal 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
13
samples/README.md
Normal 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 嵌套切换示范
|
31
samples/dynamic-druid-mybatis-sample/pom.xml
Normal file
31
samples/dynamic-druid-mybatis-sample/pom.xml
Normal 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>
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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();
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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
|
@ -0,0 +1 @@
|
||||
appender=com.p6spy.engine.spy.appender.Slf4JLogger
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
20
samples/dynamic-jdbc-template-sample/pom.xml
Normal file
20
samples/dynamic-jdbc-template-sample/pom.xml
Normal 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>
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
@ -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
|
@ -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();
|
||||
}
|
||||
}
|
22
samples/dynamic-mybatis-sample/pom.xml
Normal file
22
samples/dynamic-mybatis-sample/pom.xml
Normal 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>
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
@ -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();
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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
|
@ -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();
|
||||
}
|
||||
}
|
22
samples/dynamic-mybatisplus2-sample/pom.xml
Normal file
22
samples/dynamic-mybatisplus2-sample/pom.xml
Normal 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>
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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> {
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
22
samples/dynamic-mybatisplus3-sample/pom.xml
Normal file
22
samples/dynamic-mybatisplus3-sample/pom.xml
Normal 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>
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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> {
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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
|
@ -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);
|
||||
}
|
||||
}
|
21
samples/dynamic-nest-sample/pom.xml
Normal file
21
samples/dynamic-nest-sample/pom.xml
Normal 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>
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
@ -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();
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package com.baomidou.samples.nest.service;
|
||||
|
||||
public interface SchoolService {
|
||||
|
||||
void selectTeachersAndStudents();
|
||||
|
||||
void selectTeachersInnerStudents();
|
||||
|
||||
void addTeacherAndStudent();
|
||||
|
||||
void addTeacherAndStudentWithTx();
|
||||
}
|
@ -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();
|
||||
}
|
@ -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();
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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
|
@ -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();
|
||||
}
|
||||
}
|
22
samples/dynamic-spel-sample/pom.xml
Normal file
22
samples/dynamic-spel-sample/pom.xml
Normal 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>
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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";
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
@ -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);
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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
|
@ -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
71
samples/pom.xml
Normal 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>
|
@ -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));
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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默认数据库设置");
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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();
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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();
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
Loading…
x
Reference in New Issue
Block a user