新增: 初始化工程
commit
06aa27eac9
@ -0,0 +1,38 @@
|
|||||||
|
target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
!**/src/main/**/target/
|
||||||
|
!**/src/test/**/target/
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea/modules.xml
|
||||||
|
.idea/jarRepositories.xml
|
||||||
|
.idea/compiler.xml
|
||||||
|
.idea/libraries/
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
build/
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Mac OS ###
|
||||||
|
.DS_Store
|
@ -0,0 +1,91 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
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>
|
||||||
|
|
||||||
|
<groupId>space.caoshd</groupId>
|
||||||
|
<artifactId>multi-ds</artifactId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>3.2.1</version>
|
||||||
|
<relativePath/>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>17</maven.compiler.source>
|
||||||
|
<maven.compiler.target>17</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mybatis.spring.boot</groupId>
|
||||||
|
<artifactId>mybatis-spring-boot-starter</artifactId>
|
||||||
|
<version>3.0.3</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>druid-spring-boot-starter</artifactId>
|
||||||
|
<version>1.2.21</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
|
||||||
|
<version>4.2.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>mysql</groupId>
|
||||||
|
<artifactId>mysql-connector-java</artifactId>
|
||||||
|
<version>8.0.31</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.h2database</groupId>
|
||||||
|
<artifactId>h2</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mybatis.spring.boot</groupId>
|
||||||
|
<artifactId>mybatis-spring-boot-starter-test</artifactId>
|
||||||
|
<version>3.0.3</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<finalName>multi-ds</finalName>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<excludes>
|
||||||
|
<exclude>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
</exclude>
|
||||||
|
</excludes>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
@ -0,0 +1,11 @@
|
|||||||
|
package space.caoshd.multi_ds;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class Application {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(Application.class, args);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
package space.caoshd.multi_ds.framework.config;
|
||||||
|
|
||||||
|
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
|
||||||
|
import com.baomidou.dynamic.datasource.creator.DataSourceProperty;
|
||||||
|
import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.ApplicationListener;
|
||||||
|
import org.springframework.context.event.ContextRefreshedEvent;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import space.caoshd.multi_ds.framework.entity.DataSourceEntity;
|
||||||
|
import space.caoshd.multi_ds.framework.mapper.DatasourceMapper;
|
||||||
|
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class DataSourceRegister implements ApplicationListener<ContextRefreshedEvent> {
|
||||||
|
@Autowired
|
||||||
|
private DefaultDataSourceCreator dataSourceCreator;
|
||||||
|
@Autowired
|
||||||
|
private DatasourceMapper datasourceMapper;
|
||||||
|
@Autowired
|
||||||
|
private DynamicRoutingDataSource dynamicRoutingDataSource;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onApplicationEvent(ContextRefreshedEvent event) {
|
||||||
|
|
||||||
|
List<DataSourceEntity> dataSources = datasourceMapper.findAll();
|
||||||
|
|
||||||
|
List<String> successPoolNames = new ArrayList<>();
|
||||||
|
for (DataSourceEntity dataSourceEntity : dataSources) {
|
||||||
|
DataSourceProperty dataSourceProperty = createDataSourceProperty(dataSourceEntity);
|
||||||
|
String poolName = dataSourceProperty.getPoolName();
|
||||||
|
try {
|
||||||
|
DataSource dynamicDataSource = dataSourceCreator.createDataSource(dataSourceProperty);
|
||||||
|
dynamicRoutingDataSource.addDataSource(poolName, dynamicDataSource);
|
||||||
|
successPoolNames.add(poolName);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("create datasource: {} failed.", poolName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!successPoolNames.isEmpty()) {
|
||||||
|
log.info("{}", successPoolNames);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private DataSourceProperty createDataSourceProperty(DataSourceEntity dataSourceEntity) {
|
||||||
|
DataSourceProperty dsProperties = new DataSourceProperty();
|
||||||
|
dsProperties.setPoolName(dataSourceEntity.getDsName());
|
||||||
|
dsProperties.setUrl(dataSourceEntity.getUrl());
|
||||||
|
dsProperties.setUsername(dataSourceEntity.getUsername());
|
||||||
|
dsProperties.setPassword(dataSourceEntity.getPassword());
|
||||||
|
dsProperties.setDriverClassName(dataSourceEntity.getDriver());
|
||||||
|
return dsProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package space.caoshd.multi_ds.framework.config;
|
||||||
|
|
||||||
|
import jakarta.servlet.Filter;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import space.caoshd.multi_ds.framework.filter.DataSourceSwitchFilter;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class FilterConfig {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DataSourceSwitchFilter dataSourceSwitchFilter;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public FilterRegistrationBean<Filter> dataSourceSwitchFilterRegistration() {
|
||||||
|
FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>(dataSourceSwitchFilter);
|
||||||
|
registration.addUrlPatterns("/*");
|
||||||
|
registration.setName("dataSourceSwitchFilter");
|
||||||
|
return registration;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package space.caoshd.multi_ds.framework.entity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class DataSourceEntity {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String dsName;
|
||||||
|
|
||||||
|
private String driver;
|
||||||
|
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
private String initSql;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package space.caoshd.multi_ds.framework.filter;
|
||||||
|
|
||||||
|
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
|
||||||
|
import jakarta.servlet.*;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class DataSourceSwitchFilter implements Filter {
|
||||||
|
@Override
|
||||||
|
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException {
|
||||||
|
HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||||
|
|
||||||
|
String dsName = request.getParameter("ds_name");
|
||||||
|
if (StringUtils.hasText(dsName)) {
|
||||||
|
try {
|
||||||
|
DynamicDataSourceContextHolder.push(dsName);
|
||||||
|
filterChain.doFilter(servletRequest, servletResponse);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("switch ds error.", e);
|
||||||
|
DynamicDataSourceContextHolder.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filterChain.doFilter(servletRequest, servletResponse);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package space.caoshd.multi_ds.framework.mapper;
|
||||||
|
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Select;
|
||||||
|
import space.caoshd.multi_ds.framework.entity.DataSourceEntity;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface DatasourceMapper {
|
||||||
|
@Select("select * from datasource")
|
||||||
|
List<DataSourceEntity> findAll();
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
#mysql environment
|
||||||
|
spring:
|
||||||
|
datasource:
|
||||||
|
druid:
|
||||||
|
stat-view-servlet:
|
||||||
|
enabled: true
|
||||||
|
loginUsername: admin
|
||||||
|
loginPassword: 123456
|
||||||
|
allow:
|
||||||
|
web-stat-filter:
|
||||||
|
enabled: true
|
||||||
|
dynamic:
|
||||||
|
druid:
|
||||||
|
initial-size: 1
|
||||||
|
min-idle: 2
|
||||||
|
maxActive: 8
|
||||||
|
maxWait: 60000
|
||||||
|
timeBetweenEvictionRunsMillis: 60000
|
||||||
|
minEvictableIdleTimeMillis: 300000
|
||||||
|
validationQuery: SELECT 1
|
||||||
|
testWhileIdle: true
|
||||||
|
testOnBorrow: false
|
||||||
|
testOnReturn: false
|
||||||
|
poolPreparedStatements: true
|
||||||
|
maxPoolPreparedStatementPerConnectionSize: 20
|
||||||
|
filters: stat,wall,slf4j
|
||||||
|
connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
|
||||||
|
primary: master
|
||||||
|
strict: false
|
||||||
|
datasource:
|
||||||
|
master:
|
||||||
|
driver-class-name: org.h2.Driver
|
||||||
|
url: jdbc:h2:mem:master;INIT=RUNSCRIPT FROM 'classpath:schema.sql'
|
||||||
|
username: root
|
||||||
|
password: 123456
|
||||||
|
mybatis:
|
||||||
|
configuration:
|
||||||
|
map-underscore-to-camel-case: true
|
||||||
|
logging:
|
||||||
|
level:
|
||||||
|
com.baomidou.dynamic.datasource: debug
|
@ -0,0 +1,41 @@
|
|||||||
|
DROP TABLE IF EXISTS DATASOURCE;
|
||||||
|
CREATE TABLE DATASOURCE (
|
||||||
|
ID BIGINT,
|
||||||
|
DS_NAME VARCHAR(100),
|
||||||
|
DRIVER VARCHAR(255),
|
||||||
|
URL VARCHAR(255),
|
||||||
|
USERNAME VARCHAR(100),
|
||||||
|
PASSWORD VARCHAR(100)
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO DATASOURCE (
|
||||||
|
ID,
|
||||||
|
DS_NAME,
|
||||||
|
DRIVER,
|
||||||
|
URL,
|
||||||
|
USERNAME,
|
||||||
|
PASSWORD
|
||||||
|
) VALUES (
|
||||||
|
1,
|
||||||
|
'ds1',
|
||||||
|
'org.h2.Driver',
|
||||||
|
'jdbc:h2:mem:ds1',
|
||||||
|
'ds1',
|
||||||
|
'123456'
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO DATASOURCE (
|
||||||
|
ID,
|
||||||
|
DS_NAME,
|
||||||
|
DRIVER,
|
||||||
|
URL,
|
||||||
|
USERNAME,
|
||||||
|
PASSWORD
|
||||||
|
) VALUES (
|
||||||
|
2,
|
||||||
|
'ds2',
|
||||||
|
'org.h2.Driver',
|
||||||
|
'jdbc:h2:mem:ds2',
|
||||||
|
'ds2',
|
||||||
|
'123456'
|
||||||
|
);
|
@ -0,0 +1,10 @@
|
|||||||
|
package space.caoshd.multi_ds.entity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class TenantEntity {
|
||||||
|
private Long id;
|
||||||
|
private String username;
|
||||||
|
private String password;
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package space.caoshd.multi_ds.mapper;
|
||||||
|
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.apache.ibatis.annotations.Select;
|
||||||
|
import org.apache.ibatis.annotations.Update;
|
||||||
|
import space.caoshd.multi_ds.entity.TenantEntity;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface TenantMapper {
|
||||||
|
@Select("SELECT * FROM TENANT;")
|
||||||
|
List<TenantEntity> selectAll();
|
||||||
|
|
||||||
|
@Update("INSERT INTO TENANT (ID, USERNAME, PASSWORD) VALUES (#{tenant.id}, #{tenant.username}, #{tenant.password});")
|
||||||
|
void insert(@Param("tenant") TenantEntity tenantEntity);
|
||||||
|
|
||||||
|
@Update("CREATE TABLE TENANT (ID BIGINT, USERNAME VARCHAR(100), PASSWORD VARCHAR(100));")
|
||||||
|
void create();
|
||||||
|
|
||||||
|
@Update("DROP TABLE TENANT;")
|
||||||
|
void drop();
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
package space.caoshd.multi_ds.service;
|
||||||
|
|
||||||
|
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
|
||||||
|
import com.baomidou.dynamic.datasource.creator.DataSourceProperty;
|
||||||
|
import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;
|
||||||
|
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import space.caoshd.multi_ds.entity.TenantEntity;
|
||||||
|
import space.caoshd.multi_ds.mapper.TenantMapper;
|
||||||
|
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@SpringBootTest
|
||||||
|
public class DatasourceTest {
|
||||||
|
@Autowired
|
||||||
|
private DataSource dataSource;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DefaultDataSourceCreator dataSourceCreator;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TenantMapper tenantMapper;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createDataSource() {
|
||||||
|
|
||||||
|
DataSourceProperty dsProperties = new DataSourceProperty();
|
||||||
|
dsProperties.setPoolName("ds3");
|
||||||
|
dsProperties.setUrl("jdbc:h2:mem:ds3");
|
||||||
|
dsProperties.setDriverClassName("org.h2.Driver");
|
||||||
|
dsProperties.setUsername("root");
|
||||||
|
dsProperties.setPassword("123456");
|
||||||
|
|
||||||
|
DataSource dataSource = dataSourceCreator.createDataSource(dsProperties);
|
||||||
|
|
||||||
|
DynamicRoutingDataSource drDataSource = (DynamicRoutingDataSource) this.dataSource;
|
||||||
|
drDataSource.addDataSource(dsProperties.getPoolName(), dataSource);
|
||||||
|
|
||||||
|
Map<String, DataSource> dataSources = drDataSource.getDataSources();
|
||||||
|
for (String dsName : dataSources.keySet()) {
|
||||||
|
log.info(dsName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void switchDataSource() {
|
||||||
|
|
||||||
|
try {
|
||||||
|
DynamicDataSourceContextHolder.push("ds1");
|
||||||
|
log.info("ds1: create tenant");
|
||||||
|
tenantMapper.create();
|
||||||
|
TenantEntity tenantEntity = new TenantEntity();
|
||||||
|
tenantEntity.setId(1L);
|
||||||
|
tenantEntity.setUsername("ds2username");
|
||||||
|
tenantEntity.setPassword("ds2password");
|
||||||
|
log.info("ds1: insert tenant");
|
||||||
|
tenantMapper.insert(tenantEntity);
|
||||||
|
log.info("ds1: select tenant");
|
||||||
|
tenantMapper.selectAll();
|
||||||
|
log.info("ds1: drop tenant");
|
||||||
|
tenantMapper.drop();
|
||||||
|
} finally {
|
||||||
|
DynamicDataSourceContextHolder.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
DynamicDataSourceContextHolder.push("ds2");
|
||||||
|
log.info("ds2: create tenant");
|
||||||
|
tenantMapper.create();
|
||||||
|
TenantEntity tenantEntity = new TenantEntity();
|
||||||
|
tenantEntity.setId(2L);
|
||||||
|
tenantEntity.setUsername("ds2username");
|
||||||
|
tenantEntity.setPassword("ds2password");
|
||||||
|
log.info("ds2: insert tenant");
|
||||||
|
tenantMapper.insert(tenantEntity);
|
||||||
|
log.info("ds2: select tenant");
|
||||||
|
tenantMapper.selectAll();
|
||||||
|
log.info("ds2: drop tenant");
|
||||||
|
tenantMapper.drop();
|
||||||
|
} finally {
|
||||||
|
DynamicDataSourceContextHolder.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue