mybatisplus官方提供的多数据源和druid匹配不上,应该是springboot3之后导致spring.factories失效导致的,手写一下,测试切换数据源,事务都没问题
1.依赖
springboot版本是3.2.5
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.5</version>
</dependency>
2.注册配置
package com.ahli.filesystem.config.datasource;
import lombok.Data;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
@Configuration
@ConfigurationProperties(prefix = "spring.datasource.dynamic")
@Data
public class DynamicConfigProperties {
private String primary;
private Map<String, DataSourceProperties> datasource;
}
3.配置文件
spring:
datasource:
dynamic:
primary: write
datasource:
write:
url: jdbc:mysql://127.0.0.1:3306/filesystem?useSSL=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
read:
url: jdbc:mysql://127.0.0.1:3307/filesystem?useSSL=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
druid:
initial-size: 20
min-idle: 5
max-active: 20
max-wait: 60000
4.动态数据源
在配置类里把这个标记为primary,切换数据源才有效
package com.ahli.filesystem.config.datasource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSource();
}
}
5.数据源配置
可以修改成springboot的register bean,这里就直接写吧
package com.ahli.filesystem.config.datasource;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
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;
@Configuration
public class DynamicDataSourceConfig implements ApplicationContextAware {
@Autowired
private DynamicConfigProperties dynamicConfigProperties;
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@PostConstruct
public void registerDataSource(){
Map<String, DataSourceProperties> datasources = dynamicConfigProperties.getDatasource();
GenericApplicationContext genericContext = (GenericApplicationContext) applicationContext;
datasources.forEach((k, v)->{
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
dataSource.setUrl(v.getUrl());
dataSource.setUsername(v.getUsername());
dataSource.setPassword(v.getPassword());
dataSource.setDriverClassName(v.getDriverClassName());
genericContext.registerBean(k, DataSource.class, ()->dataSource);
});
}
@Primary
@Bean
public DataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map<Object, Object> targetDataSources = new HashMap<>();
dynamicConfigProperties.getDatasource().forEach((k, v)->{
DataSource datasource = SpringUtil.getBean(k);
if (dynamicConfigProperties.getPrimary().equals(k)) {
dynamicDataSource.setDefaultTargetDataSource(datasource);
}
targetDataSources.put(k, datasource);
});
dynamicDataSource.setTargetDataSources(targetDataSources);
return dynamicDataSource;
}
}
6.数据源holder
package com.ahli.filesystem.config.datasource;
public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public static void setDataSource(String dbName) {
contextHolder.set(dbName);
}
public static String getDataSource() {
return contextHolder.get();
}
public static void clearDataSource() {
contextHolder.remove();
}
}
7.切换数据源注解
package com.ahli.filesystem.config.datasource;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface DS {
String value() default "${spring.datasource.dynamic.primary}";
}
8.切换数据源切面
package com.ahli.filesystem.config.datasource;
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.stereotype.Component;
@Component
@Aspect
public class DynamicDataSourceAspect {
@Pointcut("@annotation(DS)")
public void dynamicDataSourcePointCut() {}
@Around("dynamicDataSourcePointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
String dataSourceKey;
Class<?> aClass = joinPoint.getTarget().getClass();
DS annotation = null;
if (aClass.isAnnotationPresent(DS.class)) {
annotation = aClass.getAnnotation(DS.class);
}
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
DS annotationMethod = null;
if (signature.getMethod().isAnnotationPresent(DS.class)) {
annotationMethod = signature.getMethod().getAnnotation(DS.class);
}
if (annotationMethod != null) {
dataSourceKey = annotationMethod.value();
} else {
dataSourceKey = annotation.value();
}
DataSourceContextHolder.setDataSource(dataSourceKey);
try {
return joinPoint.proceed();
} finally {
DataSourceContextHolder.clearDataSource();
}
}
}