├── .gitignore
├── .mvn
└── wrapper
│ ├── MavenWrapperDownloader.java
│ ├── maven-wrapper.jar
│ └── maven-wrapper.properties
├── README.md
├── pom.xml
└── src
├── main
├── java
│ └── com
│ │ └── inspur
│ │ └── dynamicdatasource
│ │ ├── DynamicDatasourceApplication.java
│ │ ├── config
│ │ ├── DynamicDataSource.java
│ │ ├── DynamicDataSourceConfig.java
│ │ ├── SpringContextHolder.java
│ │ ├── annotation
│ │ │ └── DynamicSwitchDataSource.java
│ │ └── aspect
│ │ │ └── DataSourceAspect.java
│ │ ├── controller
│ │ └── UserController.java
│ │ ├── entity
│ │ ├── DatabaseDetail.java
│ │ └── User.java
│ │ ├── mapper
│ │ ├── DatabaseDetailMapper.java
│ │ └── UserMapper.java
│ │ └── service
│ │ └── UserService.java
└── resources
│ └── application.yml
└── test
└── java
└── com
└── inspur
└── dynamicdatasource
├── DynamicDatasourceApplicationTests.java
└── datatest
├── DatabaseTest.java
└── UserServiceTest.java
/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | target/
3 | !.mvn/wrapper/maven-wrapper.jar
4 | !**/src/main/**
5 | !**/src/test/**
6 |
7 | ### STS ###
8 | .apt_generated
9 | .classpath
10 | .factorypath
11 | .project
12 | .settings
13 | .springBeans
14 | .sts4-cache
15 |
16 | ### IntelliJ IDEA ###
17 | .idea
18 | *.iws
19 | *.iml
20 | *.ipr
21 |
22 | ### NetBeans ###
23 | /nbproject/private/
24 | /nbbuild/
25 | /dist/
26 | /nbdist/
27 | /.nb-gradle/
28 | build/
29 |
30 | ### VS Code ###
31 | .vscode/
32 |
--------------------------------------------------------------------------------
/.mvn/wrapper/MavenWrapperDownloader.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | import java.net.*;
17 | import java.io.*;
18 | import java.nio.channels.*;
19 | import java.util.Properties;
20 |
21 | public class MavenWrapperDownloader {
22 |
23 | private static final String WRAPPER_VERSION = "0.5.5";
24 | /**
25 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
26 | */
27 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
28 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
29 |
30 | /**
31 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
32 | * use instead of the default one.
33 | */
34 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
35 | ".mvn/wrapper/maven-wrapper.properties";
36 |
37 | /**
38 | * Path where the maven-wrapper.jar will be saved to.
39 | */
40 | private static final String MAVEN_WRAPPER_JAR_PATH =
41 | ".mvn/wrapper/maven-wrapper.jar";
42 |
43 | /**
44 | * Name of the property which should be used to override the default download url for the wrapper.
45 | */
46 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
47 |
48 | public static void main(String args[]) {
49 | System.out.println("- Downloader started");
50 | File baseDirectory = new File(args[0]);
51 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
52 |
53 | // If the maven-wrapper.properties exists, read it and check if it contains a custom
54 | // wrapperUrl parameter.
55 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
56 | String url = DEFAULT_DOWNLOAD_URL;
57 | if(mavenWrapperPropertyFile.exists()) {
58 | FileInputStream mavenWrapperPropertyFileInputStream = null;
59 | try {
60 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
61 | Properties mavenWrapperProperties = new Properties();
62 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
63 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
64 | } catch (IOException e) {
65 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
66 | } finally {
67 | try {
68 | if(mavenWrapperPropertyFileInputStream != null) {
69 | mavenWrapperPropertyFileInputStream.close();
70 | }
71 | } catch (IOException e) {
72 | // Ignore ...
73 | }
74 | }
75 | }
76 | System.out.println("- Downloading from: " + url);
77 |
78 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
79 | if(!outputFile.getParentFile().exists()) {
80 | if(!outputFile.getParentFile().mkdirs()) {
81 | System.out.println(
82 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
83 | }
84 | }
85 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
86 | try {
87 | downloadFileFromURL(url, outputFile);
88 | System.out.println("Done");
89 | System.exit(0);
90 | } catch (Throwable e) {
91 | System.out.println("- Error downloading");
92 | e.printStackTrace();
93 | System.exit(1);
94 | }
95 | }
96 |
97 | private static void downloadFileFromURL(String urlString, File destination) throws Exception {
98 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
99 | String username = System.getenv("MVNW_USERNAME");
100 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
101 | Authenticator.setDefault(new Authenticator() {
102 | @Override
103 | protected PasswordAuthentication getPasswordAuthentication() {
104 | return new PasswordAuthentication(username, password);
105 | }
106 | });
107 | }
108 | URL website = new URL(urlString);
109 | ReadableByteChannel rbc;
110 | rbc = Channels.newChannel(website.openStream());
111 | FileOutputStream fos = new FileOutputStream(destination);
112 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
113 | fos.close();
114 | rbc.close();
115 | }
116 |
117 | }
118 |
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ShangYao/dynamic-datasource/de2d4029e0053891080268c93b8b29e0459b8a0d/.mvn/wrapper/maven-wrapper.jar
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.2/apache-maven-3.6.2-bin.zip
2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # dynamic-datasource
2 | ####业务描述:
3 | 每新增一个租户,就新建一个数据库,不同的租户的数据,会存储到各自的数据库中
4 |
5 | 不同的租户的请求需要操作各自对应的数据库。
6 |
7 | ####问题点
8 | 1. 多数据源
9 | 2. 数据源与租户的对应关系(数据库动态获取租户的数据源信息)
10 | 3. 如何区分不同用户的请求(session)
11 |
12 |
13 | 多租户业务下,根据租户id动态创建、切换数据源
14 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 2.2.0.RELEASE
9 |
10 |
11 | com.inspur
12 | dynamic-datasource
13 | 0.0.1-SNAPSHOT
14 | dynamic-datasource
15 | Demo project for Spring Boot
16 |
17 |
18 | 1.8
19 |
20 |
21 |
22 |
23 | com.baomidou
24 | mybatis-plus-boot-starter
25 | 3.2.0
26 |
27 |
28 | mysql
29 | mysql-connector-java
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | org.projectlombok
39 | lombok
40 | true
41 |
42 |
43 | org.springframework.boot
44 | spring-boot-starter-aop
45 |
46 |
47 | org.springframework.boot
48 | spring-boot-starter-web
49 |
50 |
51 | org.springframework.boot
52 | spring-boot-starter-test
53 | test
54 |
55 |
56 | org.junit.vintage
57 | junit-vintage-engine
58 |
59 |
60 |
61 |
62 | junit
63 | junit
64 | test
65 |
66 |
67 |
68 |
69 |
70 |
71 | org.springframework.boot
72 | spring-boot-maven-plugin
73 |
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/src/main/java/com/inspur/dynamicdatasource/DynamicDatasourceApplication.java:
--------------------------------------------------------------------------------
1 | package com.inspur.dynamicdatasource;
2 |
3 | import org.mybatis.spring.annotation.MapperScan;
4 | import org.springframework.boot.SpringApplication;
5 | import org.springframework.boot.autoconfigure.SpringBootApplication;
6 | import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
7 |
8 | @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
9 | @MapperScan("com.inspur.dynamicdatasource.mapper")
10 | public class DynamicDatasourceApplication {
11 |
12 | public static void main(String[] args) {
13 | SpringApplication.run(DynamicDatasourceApplication.class, args);
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/inspur/dynamicdatasource/config/DynamicDataSource.java:
--------------------------------------------------------------------------------
1 | package com.inspur.dynamicdatasource.config;
2 |
3 | import com.inspur.dynamicdatasource.entity.DatabaseDetail;
4 | import com.inspur.dynamicdatasource.mapper.DatabaseDetailMapper;
5 | import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
6 |
7 | import javax.sql.DataSource;
8 | import java.util.concurrent.ConcurrentHashMap;
9 |
10 | public class DynamicDataSource extends AbstractRoutingDataSource {
11 |
12 | /**
13 | * 缓存当前线程数据源的key(租户id)
14 | */
15 | private static final ThreadLocal CURRENT_DATASOURCE_KEY = new ThreadLocal<>();
16 | /**
17 | * 缓存租户对应的数据源
18 | * ConcurrentHashMap<租户id,数据源>
19 | */
20 | private ConcurrentHashMap