新增: 初始化工程

master
曹世达 7 months ago
commit 06aa27eac9

38
.gitignore vendored

@ -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…
Cancel
Save