新增: 初始化工程
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