目录
多数据源实现
yml配置文件
配置类
业务代码
案例演示
多数据源实现
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
initial-size: 1
min-idle: 1
max-active: 20
test-on-borrow: true
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
initial-size: 1
min-idle: 1
max-active: 20
test-on-borrow: true
driver-class-name: com.mysql.cj.jdbc.Driver
创建两个数据库分别为datasource1、datasource2,在两个数据中分别创建一张friend表
配置类
配置两个DataSource的Bean
@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;
}
}
@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();
}
@Override
public void afterPropertiesSet() {
// 为targetDataSources初始化所有数据源
Map<Object, Object> targetDataSources=new HashMap<>();
targetDataSources.put("W",dataSource1);
targetDataSources.put("R",dataSource2);
super.setTargetDataSources(targetDataSources);
// 为defaultTargetDataSource 设置默认的数据源
super.setDefaultTargetDataSource(dataSource1);
super.afterPropertiesSet();
}
}
继承AbstractRoutingDataSource类,定义一个threadlocal线程变量,并将两个数据源自动注入,在重写的afterPropertiesSet方法中(该方法是bean初始化后执行的方法),将两个数据源放入到spring管理的数据源中,通过重写的determineCurrentLookupKey方法返回W或R表示读库或写库。
业务代码
@RestController
@RequestMapping("friend")
@Slf4j
public class FrendController {
@Autowired
private FrendService frendService;
@GetMapping(value = "select")
public List<Friend> select(){
return frendService.list();
}
@GetMapping(value = "insert")
public String insert(){
Friend friend = new Friend();
friend.setName("张三");
frendService.save(friend);
return "success";
}
}
public interface FrendService {
List<Friend> list();
void save(Friend frend);
}
@Service
public class FrendImplService implements FrendService {
@Autowired
FrendMapper frendMapper;
@Override
// @WR("R") // 库2
public List<Friend> list() {
DynamicDataSource.name.set("R");
return frendMapper.list();
}
@Override
// @WR("W") // 库1
public void save(Friend frend) {
DynamicDataSource.name.set("W");
frendMapper.save(frend);
}
}
在具体的实现当中,先通过DynamicDataSource设置需要的操作的数据库。或者可以用注解的方式来实现,注解配置如下:
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface WR {
String value() default "W";
}
@Component
@Aspect
public class DynamicDataSourceAspect implements Ordered {
// 前置
@Before("within(com.dynamic.datasource.service.impl.*) && @annotation(wr)")
public void before(JoinPoint point, WR wr){
String name = wr.value();
DynamicDataSource.name.set(name);
System.out.println(name);
}
}
通过切面的方式,获取到当前方法需要操作的数据库。
案例演示
启动项目后,执行select请求
执行insert请求
查看数据库
发现datasource1有数据,datasource2无数据。
如果上述两种数据库是主从的话,此种方式可以实现主从数据库,主库写入,从库负责读。