前言
MySQL 主从可以更好的减少数据库压力,今天搞了一个小demo,只读接口让他去从库查,用注解方式实现。代码上传至码云:
https://gitee.com/yunup/some-examples
环境信息
Java 版本 1.8
SpringBoot 版本 2.2.3.RELEASE
MySQL主库 test
从库 test2
编写配置文件
spring:
datasource:
url: "jdbc:mysql://localhost:3306/test\?useUnicode=true&characterEncoding=utf-8&useAffectedRows=true\&allowMultiQueries=true&useSSL=false&serverTimezone=Asia/Shanghai"
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.zaxxer.hikari.HikariDataSource
hikari:
minimumIdle: 0
maximum-pool-size: 20
idle-timeout: 180000
dynamic-data-source:
enabled: true
slave:
url: "jdbc:mysql://localhost:3306/test2\?useUnicode=true&characterEncoding=utf-8&useAffectedRows=true\&allowMultiQueries=true&useSSL=false&serverTimezone=Asia/Shanghai"
username: root
password: root
动态数据源类
创建 DynamicDataSource 类,继承自 AbstractRoutingDataSource ,用于判断使用哪个数据源
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<DataSourceType> dataSourceTypeThreadLocal = new ThreadLocal<>();
public static void removeDataSourceType() {
dataSourceTypeThreadLocal.remove();
}
public static void setDataSourceType(DataSourceType dataSourceType) {
if (dataSourceTypeThreadLocal.get() == null) {
dataSourceTypeThreadLocal.set(dataSourceType);
}
}
@Override
protected Object determineCurrentLookupKey() {
return dataSourceTypeThreadLocal.get() == null ? DataSourceType.Master : dataSourceTypeThreadLocal.get();
}
}
多数据源配置
首先,配置多个数据源,配置文件中 dynamic-data-source的enabled为true,表示开启动态数据源,则注入主数据源和从数据源:
@Bean({"masterDataSource"})
@ConditionalOnProperty(name = {"dynamic-data-source.enabled"}, havingValue = "true")
public DataSource masterDataSource() throws CloneNotSupportedException {
this.log.info("init masterDataSource...");
// 此处省略设置参数的代码
return new HikariDataSource(properties);
}
@Bean({"slaverDataSource"})
@ConditionalOnProperty(name = {"dynamic-data-source.enabled"}, havingValue = "true")
public DataSource slaverDataSource() throws CloneNotSupportedException {
this.log.info("init slaverDataSource...");
// 此处省略设置参数的代码
return new HikariDataSource(properties);
}
@Bean
@Primary
@DependsOn({"masterDataSource", "slaverDataSource"})
@ConditionalOnProperty(name = {"dynamic-data-source.enabled"}, havingValue = "true")
public DynamicDataSource dataSource(DataSource masterDataSource, DataSource slaverDataSource) {
this.log.info("init dynamicDataSource...");
DynamicDataSource source = new DynamicDataSource();
Map<Object, Object> map = new HashMap<>();
map.put(DataSourceType.Master, masterDataSource);
map.put(DataSourceType.Slave, slaverDataSource);
source.setTargetDataSources(map);
source.setDefaultTargetDataSource(masterDataSource);
return source;
}
创建注解类
创建两个注解类,如果是查询接口,接口上就标注 DBReadOnly,让他去查询从库
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DBReadOnly {
}
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DBWriteable {
}
编写切面代码
@Aspect
@Component
@ConditionalOnProperty(name = {"dynamic-data-source.enabled"}, havingValue = "true")
public class DataSourceAspect {
@Around("@annotation(annotation)")
public Object processDBReadOnly(ProceedingJoinPoint joinPoint, DBReadOnly annotation) {
DynamicDataSource.setDataSourceType(DataSourceType.Slave);
Object var3 = null;
try {
var3 = joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
} finally {
DynamicDataSource.removeDataSourceType();
}
return var3;
}
@Around("@annotation(annotation)")
public Object processDBWriteable(ProceedingJoinPoint joinPoint, DBWriteable annotation) {
DynamicDataSource.setDataSourceType(DataSourceType.Master);
Object var3 = null;
try {
var3 = joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
} finally {
DynamicDataSource.removeDataSourceType();
}
return var3;
}
}
测试效果
在控制器上对于两个请求进行标注,findAll 请求去查 test(主) 数据库,findAllSecond 去查 test2(从) 数据库。
@GetMapping("/findAll")
@DBWriteable
public List<Users> findAll() {
List<Users> users = usersService.findAll();
return users;
}
@GetMapping("/findAllSecond")
@DBReadOnly
public List<Users> findAllSecond() {
List<Users> users = usersService.findAll();
return users;
}
findAll 结果:[{“userId”:1,“userName”:“张三”},{“userId”:2,“userName”:“李四”},{“userId”:3,“userName”:“王五”}]
findAllSecond 结果: [{“userId”:1,“userName”:“Lucy”},{“userId”:2,“userName”:“Dick”},{“userId”:3,“userName”:“Angel”}]
编码中,遇到了循环依赖的问题,可见:https://blog.csdn.net/K_Tang/article/details/83117354