1、application-dev.properties配置
spring.datasource.primary.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.primary.jdbc-url=jdbc:mysql:///project_datahub?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
spring.datasource.primary.username=root
spring.datasource.primary.password=root
spring.datasource.secondary.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.secondary.jdbc-url=jdbc:mysql:///project_datahub_second?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
spring.datasource.secondary.username=root
spring.datasource.secondary.password=root
2、不同请求,不同线程,数据源的隔离类
/**
* @Description 切换数据源
* @Author lxk
* @version V1.0.0
*/
public class DBContextHolder{
/**
* ThreadLocal数据隔离,但是并非数据的复制,而是在每一个线程中创建一个新的数据对象,然后每一个线程使用的是不一样的
*/
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
public static void setDataSource(String dataSource) {
CONTEXT_HOLDER.set(dataSource);
}
public static String getDataSource() {
return CONTEXT_HOLDER.get();
}
public static void clearDataSource() {
CONTEXT_HOLDER.remove();
}
}
3、spring官方AbstractRoutingDataSource核心类实现
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.lang.Nullable;
public class MyRoutingDataSource extends AbstractRoutingDataSource {
@Nullable
@Override
protected Object determineCurrentLookupKey() {
return DBContextHolder.getDataSource();
}
}
4、常量配置
/**
* @Description
* @Author lxk
* @version V1.0.0
*/
public interface DataSourceNames {
String FIRST = "first";
String SECOND = "second";
}
5、数据源配置
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
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 javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* @Description
* @Author lxk
* @version V1.0.0
*/
@Configuration
public class DataSourceConfig {
@Bean(name = "firstDataSource")
@Qualifier("firstDataSource")
@ConfigurationProperties(prefix="spring.datasource.primary")
public DataSource firstDataSource() {
System.out.println("primary db built");
return DataSourceBuilder.create().build();
}
@Bean(name = "secondDataSource")
@Qualifier("secondDataSource")
@ConfigurationProperties(prefix="spring.datasource.secondary")
public DataSource secondDataSource() {
System.out.println("secondary db built");
return DataSourceBuilder.create().build();
}
@Primary
@Bean
public DataSource dataSource(DataSource firstDataSource, DataSource secondDataSource) {
Map<Object, Object> targetDataSources = new HashMap<>(5);
targetDataSources.put(DataSourceNames.FIRST, firstDataSource);
targetDataSources.put(DataSourceNames.SECOND, secondDataSource);
MyRoutingDataSource myRoutingDataSource = new MyRoutingDataSource();
// 如果没有指定数据源自动切换主数据源
myRoutingDataSource.setDefaultTargetDataSource(firstDataSource);
myRoutingDataSource.setTargetDataSources(targetDataSources);
return myRoutingDataSource;
}
}
6、aop通过注解,动态切换数据源
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* 多数据源,切面处理类
* @Author lxk
* @version V1.0.0
*/
@Aspect
@Component
public class DataSourceAspect implements Ordered {
@Pointcut("@annotation(com.magus.datahub.master.orghandler.config.CurDataSource)")
public void dataSourcePointCut() {
}
@Before("dataSourcePointCut()")
public void doBefore(JoinPoint point){
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
CurDataSource ds = method.getAnnotation(CurDataSource.class);
if (ds == null) {
DBContextHolder.setDataSource(DataSourceNames.FIRST);
} else {
DBContextHolder.setDataSource(ds.name());
}
}
@After("dataSourcePointCut()")
public void after(JoinPoint point) {
//清理掉当前设置的数据源,让默认的数据源不受影响
DBContextHolder.clearDataSource();
}
@Override
public int getOrder() {
return -1;
}
}
7、动态切换数据源,示例
controller示例
@RequestMapping("/ehr")
public class EhrController {
@Autowired
private EhrService ehrService;
@GetMapping("/test/switch")
public ResponseEntity<List<MasterCompany>> ehrServiceSwitch(){
return ResponseEntity.ok(ehrService.findAllSwitch());
}
@GetMapping("/test")
public ResponseEntity<List<MasterCompany>> ehrService(){
return ResponseEntity.ok(ehrService.findAll());
}
}
service示例
@Service
public class EhrService {
@Autowired
private MasterCompanyRepository companyRepository;
public List<MasterCompany> findAll(){
return companyRepository.findAll();
}
@CurDataSource(name = DataSourceNames.SECOND)
public List<MasterCompany> findAllSwitch(){
return companyRepository.findAll();
}
}
事务控制,可参考 https://www.cnblogs.com/jpfss/p/8295692.html