本文章使用 spring boot3、 java21、maven工程
1、pom依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-3-starter</artifactId>
<version>1.2.21</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.5</version>
</dependency>
<!-- tdengine taos驱动 -->
<dependency>
<groupId>com.taosdata.jdbc</groupId>
<artifactId>taos-jdbcdriver</artifactId>
<version>3.0.0</version>
</dependency>
2、yml配置
server:
port: 31001
servlet:
context-path: /dataCapture
spring:
datasource:
jsdb:
driver-class-name: com.taosdata.jdbc.TSDBDriver
url: jdbc:TAOS://192.168.1.60:6030/data_capture?charset=utf8
username: root
password: taosdata
kbdb:
driver-class-name: com.taosdata.jdbc.TSDBDriver
url: jdbc:TAOS://192.168.1.60:6030/data_capture_kb?charset=utf8
username: root
password: taosdata
type: com.alibaba.druid.pool.DruidDataSource
druid:
initial-size: 2
max-active: 30
min-idle: 2
max-wait: 5000
pool-prepared-statements: true
validation-query: select 1
validation-query-timeout: 1
test-on-borrow: false
test-on-return: true
test-while-idle: true
time-between-eviction-runs-millis: 10000
min-evictable-idle-time-millis: 30001
filters: stat
mybatis-plus:
type-aliases-package: com.hiabr.web
mapper-locations: classpath:mapper/**/*Mapper.xml
configuration:
default-statement-timeout: 120
map-underscore-to-camel-case: true
cache-enabled: false
call-setters-on-nulls: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
3、核心配置类
import com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceBuilder;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.hiabr.web.common.DynamicRoutingDataSource;
import com.hiabr.web.enums.DataSourceKey;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Configuration
@MapperScan(basePackages = "com.hiabr.web.dao")
public class DynamicDataSourceConfiguration {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.jsdb")
public DataSource dbMaster() {
return DruidDataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.kbdb")
public DataSource dbSlave1() {
return DruidDataSourceBuilder.create().build();
}
@Bean
public DataSource dynamicDataSource() {
DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();
dataSource.setDefaultTargetDataSource(dbMaster());
Map<Object, Object> dataSourceMap = new HashMap<>(4);
dataSourceMap.put(DataSourceKey.DB_MASTER, dbMaster());
dataSourceMap.put(DataSourceKey.DB_SLAVE1, dbSlave1());
dataSource.setTargetDataSources(dataSourceMap);
return dataSource;
}
// 使用yml配置文件中mybatis-plus全局配置
@Bean
@ConfigurationProperties(prefix = "mybatis-plus.configuration")
public MybatisConfiguration mybatisConfiguration() {
return new MybatisConfiguration();
}
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dynamicDataSource());
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/**/*Mapper.xml"));
// 设置mybatis-plus全局配置,不配置会出现例如下划线无法转驼峰,日志等
sqlSessionFactoryBean.setConfiguration(mybatisConfiguration());
return sqlSessionFactoryBean.getObject();
}
@Bean
public SqlSessionTemplate sqlSessionTemplate() throws Exception {
return new SqlSessionTemplate(sqlSessionFactory());
}
/**
* 事务管理
*
* @return 事务管理实例
*/
@Bean
public PlatformTransactionManager platformTransactionManager() {
return new DataSourceTransactionManager(dynamicDataSource());
}
}
4、DynamicRoutingDataSource 类
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* @description:
* @author: hanwei
* @date: 2025/5/7
*/
@Slf4j
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
// log.info("当前数据源:{}"+ DynamicDataSourceContextHolder.get());
return DynamicDataSourceContextHolder.get();
}
}
5、自定义注解、aop切面
import com.hiabr.web.enums.DataSourceKey;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// @Target:注解的作用目标
// @Target(ElementType.TYPE) //接口、类、枚举、注解
// @Target(ElementType.METHOD) // 方法
/**
* @description:
* @author: hanwei
* @date: 2025/5/7
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TargetDataSource {
DataSourceKey dataSourceKey();
}
public enum DataSourceKey {
DB_MASTER,
DB_SLAVE1
}
import com.hiabr.web.annaotation.TargetDataSource;
import com.hiabr.web.enums.DataSourceKey;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* @description:
* @author: hanwei
* @date: 2025/5/7
*/
@Aspect
@Order(1)
@Component
@Slf4j
public class DynamicDataSourceAspect {
@Pointcut("execution(* com.hiabr.web.service.*.*(..))")
public void pointCut() {
}
/**
* 执行方法前更换数据源
*
* @param joinPoint 切点
* @param targetDataSource 动态数据源
*/
@Before("@annotation(targetDataSource)")
public void doBefore(JoinPoint joinPoint, TargetDataSource targetDataSource) {
DataSourceKey dataSourceKey = targetDataSource.dataSourceKey();
if (dataSourceKey == DataSourceKey.DB_SLAVE1) {
log.info(String.format("设置数据源为 %s", DataSourceKey.DB_SLAVE1));
DynamicDataSourceContextHolder.set(DataSourceKey.DB_SLAVE1);
} else {
log.info(String.format("使用默认数据源 %s", DataSourceKey.DB_MASTER));
DynamicDataSourceContextHolder.set(DataSourceKey.DB_MASTER);
}
}
/**
* 执行方法后清除数据源设置
*
* @param joinPoint 切点
* @param targetDataSource 动态数据源
*/
@After("@annotation(targetDataSource)")
public void doAfter(JoinPoint joinPoint, TargetDataSource targetDataSource) {
log.info(String.format("当前数据源 %s 执行清理方法", targetDataSource.dataSourceKey()));
DynamicDataSourceContextHolder.clear();
}
}
6、DynamicDataSourceContextHolder 类
import com.hiabr.web.enums.DataSourceKey;
import org.apache.commons.lang3.RandomUtils;
/**
* @description:
* @author: hanwei
* @date: 2025/5/7
*/
public class DynamicDataSourceContextHolder {
private static final ThreadLocal<DataSourceKey> currentDatesource = new ThreadLocal<>();
/**
* 清除当前数据源
*/
static void clear() {
currentDatesource.remove();
}
/**
* 获取当前使用的数据源
*
* @return 当前使用数据源的ID
*/
static DataSourceKey get() {
return currentDatesource.get();
}
/**
* 设置当前使用的数据源
*
* @param value 需要设置的数据源ID
*/
static void set(DataSourceKey value) {
currentDatesource.set(value);
}
}
7、通过注解切换数据源
@TargetDataSource(dataSourceKey = DataSourceKey.DB_SLAVE1)