主从库双数据源

This commit is contained in:
zc
2026-05-07 15:58:13 +08:00
parent 3113ba2542
commit 470a2dbe6e
17 changed files with 374 additions and 33 deletions

View File

@@ -8,6 +8,7 @@ import lombok.extern.slf4j.Slf4j;
import org.dromara.x.file.storage.spring.EnableFileStorage;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -28,7 +29,7 @@ import top.continew.starter.web.model.R;
@EnableGlobalResponse
@EnableCrudRestController
@RestController
@SpringBootApplication
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@RequiredArgsConstructor
@EnableScheduling
public class WmsAdminApplication {
@@ -46,4 +47,4 @@ public class WmsAdminApplication {
return R.ok(projectProperties);
}
}
}

View File

@@ -0,0 +1,110 @@
package top.wms.admin.config;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import top.wms.admin.common.config.mybatis.DynamicRoutingDataSource;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* 动态数据源配置
*
* @author Admin
* @since 2024/12/22
*/
@Configuration
public class DynamicDataSourceConfig {
@Value("${spring.datasource.wms.url}")
private String wmsUrl;
@Value("${spring.datasource.wms.username}")
private String wmsUsername;
@Value("${spring.datasource.wms.password}")
private String wmsPassword;
@Value("${spring.datasource.wms2.url}")
private String wms2Url;
@Value("${spring.datasource.wms2.username}")
private String wms2Username;
@Value("${spring.datasource.wms2.password}")
private String wms2Password;
/**
* 主数据源wms
*/
@Bean("wmsDataSource")
public DataSource wmsDataSource() {
HikariConfig config = new HikariConfig();
config.setDriverClassName("com.p6spy.engine.spy.P6SpyDriver");
config.setJdbcUrl(wmsUrl);
config.setUsername(wmsUsername);
config.setPassword(wmsPassword);
config.setMaximumPoolSize(20);
config.setConnectionTimeout(30000);
config.setIdleTimeout(600000);
config.setKeepaliveTime(30000);
config.setMaxLifetime(1800000);
return new HikariDataSource(config);
}
/**
* 从数据源wms2
*/
@Bean("wms2DataSource")
public DataSource wms2DataSource() {
HikariConfig config = new HikariConfig();
config.setDriverClassName("com.p6spy.engine.spy.P6SpyDriver");
config.setJdbcUrl(wms2Url);
config.setUsername(wms2Username);
config.setPassword(wms2Password);
config.setMaximumPoolSize(20);
config.setConnectionTimeout(30000);
config.setIdleTimeout(600000);
config.setKeepaliveTime(30000);
config.setMaxLifetime(1800000);
return new HikariDataSource(config);
}
/**
* 动态数据源
*/
@Bean("dynamicDataSource")
@Primary
public DataSource dynamicDataSource(DataSource wmsDataSource, DataSource wms2DataSource) {
DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
// 设置默认数据源
dynamicRoutingDataSource.setDefaultTargetDataSource(wmsDataSource);
// 设置数据源映射
Map<Object, Object> dataSourceMap = new HashMap<>();
dataSourceMap.put("wms", wmsDataSource);
dataSourceMap.put("wms2", wms2DataSource);
dynamicRoutingDataSource.setTargetDataSources(dataSourceMap);
// 必须调用此方法,否则首次获取数据源时会失败
dynamicRoutingDataSource.afterPropertiesSet();
return dynamicRoutingDataSource;
}
/**
* 事务管理器
*/
@Bean
public PlatformTransactionManager transactionManager(DataSource dynamicDataSource) {
return new DataSourceTransactionManager(dynamicDataSource);
}
}

View File

@@ -0,0 +1,59 @@
package top.wms.admin.config;
import cn.dev33.satoken.stp.StpUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import top.wms.admin.common.constant.DataSourceContextHolder;
import top.wms.admin.common.context.UserContext;
import top.wms.admin.common.context.UserContextHolder;
/**
* 动态数据源拦截器
* 在每次请求时根据当前登录用户切换数据源
*
* @author Admin
* @since 2024/12/22
*/
@Component
public class DynamicDataSourceInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
String requestUri = request.getRequestURI();
// 认证相关接口始终使用主库wms确保用户验证和权限获取在主库进行
if (requestUri.contains("/auth/login") || requestUri.contains("/auth/user/info") || requestUri
.contains("/auth/user/route") || requestUri.contains("/auth/logout")) {
DataSourceContextHolder.setDataSource("wms");
return true;
}
// 如果用户已登录,从用户上下文获取数据源并设置
if (StpUtil.isLogin()) {
try {
UserContext userContext = UserContextHolder.getContext();
if (userContext != null && userContext.getDataSource() != null) {
DataSourceContextHolder.setDataSource(userContext.getDataSource());
}
} catch (Exception e) {
// 如果获取用户上下文失败,使用默认数据源
DataSourceContextHolder.clearDataSource();
}
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
@Nullable Exception ex) throws Exception {
// 请求结束后清除数据源上下文
DataSourceContextHolder.clearDataSource();
}
}

View File

@@ -0,0 +1,27 @@
package top.wms.admin.config;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* WebMvc 配置
*
* @author Admin
* @since 2024/12/22
*/
@Configuration
@RequiredArgsConstructor
public class WebMvcConfig implements WebMvcConfigurer {
private final DynamicDataSourceInterceptor dynamicDataSourceInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册动态数据源拦截器,优先级高于其他拦截器
registry.addInterceptor(dynamicDataSourceInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/error", "/doc.html", "/webjars/**", "/swagger-ui/**", "/swagger-resources/**", "/*/api-docs/**", "/favicon.ico");
}
}

View File

@@ -16,33 +16,36 @@ spring:
--- ### 数据源配置
spring.datasource:
type: com.zaxxer.hikari.HikariDataSource
# 请务必提前创建好名为 wms_admin 的数据库,如果使用其他数据库名请注意同步修改 DB_NAME 配置
url: jdbc:p6spy:mysql://127.0.0.1:3306/wms?serverTimezone=Asia/Shanghai&useSSL=false&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&autoReconnect=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
username: root
# password: root
password: test123$
# PostgreSQL 配置
# url: jdbc:p6spy:mysql://192.168.2.30:${DB_PORT:3306}/continew?serverTimezone=Asia/Shanghai&useSSL=true&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&autoReconnect=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
# username: ${DB_USER:root}
# password: ${DB_PWD:SQLsql123}
# url: jdbc:p6spy:mysql://81.68.71.142:${DB_PORT:3306}/continew?serverTimezone=Asia/Shanghai&useSSL=true&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&autoReconnect=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
# username: ${DB_USER:root}
# password: ${DB_PWD:MYsql12@}
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
# Hikari 连接池配置
hikari:
# 最大连接数量(默认 10根据实际环境调整
# 注意:当连接达到上限,并且没有空闲连接可用时,获取连接将在超时前阻塞最多 connectionTimeout 毫秒
maximum-pool-size: 20
# 获取连接超时时间(默认 30000 毫秒30 秒)
connection-timeout: 30000
# 空闲连接最大存活时间(默认 600000 毫秒10 分钟)
idle-timeout: 600000
# 保持连接活动的频率,以防止它被数据库或网络基础设施超时。该值必须小于 maxLifetime默认 0禁用
keepaliveTime: 30000
# 连接最大生存时间(默认 1800000 毫秒30 分钟)
max-lifetime: 1800000
# 主数据源wms
wms:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
url: jdbc:p6spy:mysql://127.0.0.1:3306/wms?serverTimezone=Asia/Shanghai&useSSL=false&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&autoReconnect=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
username: root
# password: root
password: test123$
# Hikari 连接池配置
hikari:
maximum-pool-size: 20
connection-timeout: 30000
idle-timeout: 600000
keepaliveTime: 30000
max-lifetime: 1800000
# 从数据源wms2
wms2:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
url: jdbc:p6spy:mysql://127.0.0.1:3306/wms2?serverTimezone=Asia/Shanghai&useSSL=false&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&autoReconnect=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
username: root
# password: root
password: test123$
# Hikari 连接池配置
hikari:
maximum-pool-size: 20
connection-timeout: 30000
idle-timeout: 600000
keepaliveTime: 30000
max-lifetime: 1800000
## Liquibase 配置
spring.liquibase:
# 是否启用