本博客姊妹篇
# spring配置
spring:
# 数据源配置
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/boot_codegen?useUnicode=true&characterEncoding=UTF8&serverTimezone=GMT%2B8&useSSL=false
username: root
password: root
initial-size: 10
min-idle: 10
max-active: 100
max-wait: 60000
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
validation-query: select 1
test-while-idle: true
test-on-borrow: false
test-on-return: false
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
web-stat-filter:
enabled: true
url-pattern: /*
exclusions: '*.js,*.css,*.gif,*.png,*.jpg,*.ico,/druid/*'
stat-view-servlet:
enabled: true
url-pattern: /druid/*
login-username: admin
login-password: 123456
filter:
stat:
enabled: true
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
enabled: true
config:
multi-statement-allow: true
package com.qiangesoft.datasourcepro.core;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* 多数据源配置
*
* @author qiangesoft
* @date 2024-03-14
*/
@Slf4j
@Configuration
public class DataSourceConfiguration {
@Bean
public DruidDataSource defaultDataSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean
@Primary
public DynamicDataSource dynamicDataSource(DruidDataSource defaultDataSource) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("default", defaultDataSource);
return new DynamicDataSource(defaultDataSource, targetDataSources);
}
}
package com.qiangesoft.datasourcepro.core;
import com.alibaba.druid.pool.DruidDataSource;
import com.qiangesoft.datasourcepro.entity.BcgDataSource;
import com.qiangesoft.datasourcepro.service.IBcgDataSourceService;
import com.qiangesoft.datasourcepro.utils.SpringUtil;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import java.util.Map;
/**
* 动态数据源
*
* @author qiangesoft
* @date 2024-03-14
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
private Map<Object, Object> dynamicTargetDataSources;
private Object dynamicDefaultTargetDataSource;
public DynamicDataSource(DruidDataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
super.setDefaultTargetDataSource(defaultTargetDataSource);
super.setTargetDataSources(targetDataSources);
super.afterPropertiesSet();
this.dynamicTargetDataSources = targetDataSources;
this.dynamicDefaultTargetDataSource = defaultTargetDataSource;
}
@Override
protected Object determineCurrentLookupKey() {
String datasourceId = DataSourceContext.getDataSource();
if (datasourceId != null && this.dynamicTargetDataSources.containsKey(datasourceId)) {
return datasourceId;
}
return null;
}
/**
* 添加数据源
*
* @param dataSourceId
*/
public void addDataSource(String dataSourceId) {
if ("default".equals(dataSourceId)) {
return;
}
int initialSize = ((DruidDataSource) dynamicDefaultTargetDataSource).getInitialSize();
BcgDataSource bcgDataSource = SpringUtil.getBean(IBcgDataSourceService.class).getById(Long.valueOf(dataSourceId));
if (bcgDataSource == null) {
throw new RuntimeException("数据源配置不存在");
}
String datasourceId = String.valueOf(bcgDataSource.getId());
Map<Object, Object> dynamicTargetDataSources = this.dynamicTargetDataSources;
if (!dynamicTargetDataSources.containsKey(datasourceId)) {
DruidDataSource dataSourceInstance = DataSourceBuilder.create()
.driverClassName(bcgDataSource.getDriverClassName())
.url(bcgDataSource.getUrl())
.username(bcgDataSource.getUsername())
.password(bcgDataSource.getPassword())
.type(DruidDataSource.class)
.build();
dynamicTargetDataSources.put(datasourceId, dataSourceInstance);
// 关键一步:将TargetDataSources中的连接信息放入resolvedDataSources管理
super.afterPropertiesSet();
}
}
}
package com.qiangesoft.datasourcepro.core;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.Objects;
/**
* 多数据源处理
*
* @author qiangesoft
* @date 2024-03-14
*/
@Slf4j
@Order(1)
@Aspect
@Component
public class DataSourceAspect {
/**
* 切点
*/
@Pointcut("@annotation(com.qiangesoft.datasourcepro.core.DataSource)")
public void pointCut() {
}
/**
* 通知
*
* @param joinPoint
* @return
* @throws Throwable
*/
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
DataSource dataSource = this.getDataSource(joinPoint);
if (dataSource == null) {
DataSourceContext.setDataSource("default");
} else {
DataSourceContext.setDataSource(dataSource.value());
}
try {
return joinPoint.proceed();
} finally {
DataSourceContext.removeDataSource();
}
}
/**
* 获取数据源
*
* @param joinPoint
* @return
*/
public DataSource getDataSource(ProceedingJoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
// 方法上查找注解
DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);
if (Objects.nonNull(dataSource)) {
return dataSource;
}
// 类上查找注解
return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);
}
}
package com.qiangesoft.datasourcepro.core;
import com.qiangesoft.datasourcepro.utils.SpringUtil;
/**
* 数据源上下文
*
* @author qiangesoft
* @date 2024-03-14
*/
public class DataSourceContext {
/**
* 线程本地变量:数据源
*/
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
/**
* 设置数据源的变量
*/
public static void setDataSource(String dataSourceId) {
SpringUtil.getBean(DynamicDataSource.class).addDataSource(dataSourceId);
CONTEXT_HOLDER.set(dataSourceId);
}
/**
* 获得数据源的变量
*/
public static String getDataSource() {
return CONTEXT_HOLDER.get();
}
/**
* 清空数据源变量
*/
public static void removeDataSource() {
CONTEXT_HOLDER.remove();
}
}
package com.qiangesoft.datasourcepro.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qiangesoft.datasourcepro.core.DataSource;
import com.qiangesoft.datasourcepro.core.DataSourceContext;
import com.qiangesoft.datasourcepro.entity.BcgDataSource;
import com.qiangesoft.datasourcepro.mapper.BcgDataSourceMapper;
import com.qiangesoft.datasourcepro.service.IBcgDataSourceService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
/**
*
* 数据源 服务实现类
*
*
* @author qiangesoft
* @since 2024-03-13
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class BcgBcgDataSourceServiceImpl extends ServiceImpl<BcgDataSourceMapper, BcgDataSource> implements IBcgDataSourceService {
@Override
public List<BcgDataSource> changeDataSource(String datasourceId) {
try {
DataSourceContext.setDataSource(datasourceId);
return this.list();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("数据源切换失败");
} finally {
DataSourceContext.removeDataSource();
}
}
@DataSource(value = "1")
@Override
public List<BcgDataSource> changeDataSourceByAnnotation() {
return this.list();
}
}