SpringBoot 如何实现多数据源
第一步:配置yml
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
datasource1:
url: jdbc:mysql://127.0.0.1:3306/datasource1?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
datasource2:
url: jdbc:mysql://127.0.0.1:3306/datasource2?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
第二步:创建一个配置类
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.datasource1")
public DataSource dataSource1() {
// 底层会自动拿到spring.datasource中的配置, 创建一个DruidDataSource
return DruidDataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.datasource2")
public DataSource dataSource2() {
// 底层会自动拿到spring.datasource中的配置, 创建一个DruidDataSource
return DruidDataSourceBuilder.create().build();
}
//事务管理器
@Bean
public DataSourceTransactionManager transactionManager1(DynamicDataSource dataSource){
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
@Bean
public DataSourceTransactionManager transactionManager2(DynamicDataSource dataSource){
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
}
第三步:继承AbstractRoutingDataSource 重新determineTargetDataSource方法
@Component
@Primary // 将该Bean设置为主要注入Bean
public class DynamicDataSource extends AbstractRoutingDataSource {
// 当前使用的数据源标识
public static ThreadLocal<String> name=new ThreadLocal<>();
// 注入数据源
@Autowired
DataSource dataSource1;
@Autowired
DataSource dataSource2;
// 返回当前数据源标识
@Override
protected Object determineCurrentLookupKey() {
return name.get();
}
/*
* 此方法在是Spring容器启动时执行,目的是将自定义的数据源放入targetDataSources 这个map中
* 通过父类的setTargetDataSources方法设置成员变量,
* 注意:必须调用父类的afterPropertiesSet()方法,这个方法会将targetDataSources赋给resolvedDataSources
* 切换数据时会从resolvedDataSources中查找
*
* */
@Override
public void afterPropertiesSet() {
// 为targetDataSources初始化所有数据源
Map<Object, Object> targetDataSources=new HashMap<>();
targetDataSources.put("W",dataSource1);
targetDataSources.put("R",dataSource2);
// 赋给成员变量targetDataSources
super.setTargetDataSources(targetDataSources);
// 为defaultTargetDataSource 设置默认的数据源
super.setDefaultTargetDataSource(dataSource1);
//调用父类的afterPropertiesSet()方法
super.afterPropertiesSet();
}
}
第四步: 多数据源切换方式,AOP+自定义注解
// 自定义注解
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface WR {
// 默认值为W
String value() default "W";
}
//AOP
@Component
@Aspect
public class DynamicDataSourceAspect implements Ordered {
// 前置
@Before("within(com.xxxxx.service.impl.*) && @annotation(wr)")
public void before(JoinPoint point, WR wr){
// 获取注解的值
String name = wr.value();
// 设置使用那个数据源
DynamicDataSource.name.set(name);
}
}
第五步:测试
@Service
public class UserService implements UserService {
@Autowired
UserMapper userMapper;
@Override
@WR("R") // 库2
public List<User> list() {
return userMapper.list();
}
@Override
@WR("W") // 库1
public void save(User user) {
userMapper.save(user);
}
}