目录
动态切换数据源原理:
由于我们项目中由于数据量会越来越大,MySQL效率就会低下,于是我们做了动态的切换数据源操作,这样做是为了防止MySQL效率低下问题.
首先定义一个子类继承AbstractRoutingDataSource数据源接口,当项目启动的时候,会加载系统中的多个数据源配置,当数据源加载成功之后,调用afterPropertiesSet方法把数据源加载到map集合中,准备数据源路由操作,然后定义自定义注解用于在执行sql的时候选择数据源,使用AOP的前置通知定义切面,在方法执行的时候把自定义注解中的key存储到ThreadLocal中,在真实调用的时候会调用determineCurrentLookupKey方法根据选择的key加载相关的数据源,这样就完成了动态数据源的切换。
废话不多说下面上代码:
动态切换数据源使用:
配置完整pom文件
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.10</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.6</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!--mybatis逆向工程插件-->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version>
<configuration>
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
</plugin>
</plugins>
</build>
配置appcation.yml 文件:
server:
port: 8080
spring:
application:
name: datasourcecheck
datasource:
ds1:
url: jdbc:mysql://127.0.0.1:3306/ds1? useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&useSSL=false
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: [ stat,wall,log4j ]
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
ds2:
url: jdbc:mysql://127.0.0.1:3306/ds2? useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&useSSL=false
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: [ stat,wall,log4j ]
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
# 切换Druid数据源
type: com.alibaba.druid.pool.DruidDataSource
# thymeleaf:
# prefix: classpath:/aaa
# suffix: .txt
mybatis:
config-location: classpath:sqlMapConfig.xml
mapper-locations: classpath:mapper/*.xml
创建一个sqlMapConfig.xml文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
</configuration>
启动类DatasourcecheckApp类:
@SpringBootApplication
@MapperScan("com.dk.mapper")
public class DatasourcecheckApp {
public static void main(String[] args) {
SpringApplication.run(DatasourcecheckApp.class, args);
}
}
创建一个WR.java类这个类是做一个默认选择数据源作用的:
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface WR {
String value()default "ds1";
}
创建一个aop的包,包里创建一个WRaop.java的类,这里是使用Spring框架的AOP(面向切面编程)。WR
注解标记的方法之前,从注解中获取一个值,并将这个值设置到某个数据源管理类中,可以用于动态切换数据源。。
@Component
@Aspect
public class WRaop {
@Before("within(com.dk.service.impl.*)&&@annotation(wr)")
public void before(JoinPoint joinPoint, WR wr){
String value = wr.value();
DyDatasource.t.set(value);
}
}
创建一个config的包,包下创建了两个配置类 DruidDatasourceConfig.java和DyDatasource .java类
@Configuration
public class DruidDatasourceConfig {
@Bean("ds1")
@ConfigurationProperties(prefix = "spring.datasource.ds1")
public DataSource db1DataSource1() {
return new DruidDataSource();
}
@Bean("ds2")
@ConfigurationProperties(prefix = "spring.datasource.ds2")
public DataSource db1DataSource2() {
return new DruidDataSource();
}
}
DyDatasource .java类,这里它继承了AbstractRoutingDataSource
类来创建一个动态数据源路由,实现在运行时根据请求的上下文动态地切换数据源。通常这种机制也可以用于读写分离、分库分表等场景.
@Component //Spring会自动检测并注册这个类为一个bean
@Primary //表示当存在多个相同类型的bean时,这个bean将作为首选的bean。在有多个数据源配置的情况下,这个DyDatasource将被优先使用
public class DyDatasource extends AbstractRoutingDataSource {
public static ThreadLocal<String> t = new ThreadLocal<>();
@Autowired
private DataSource ds1;
@Autowired
private DataSource ds2;
//返回数据源标识 通过扩展这个方法来实现数据源的切换
@Override
protected Object determineCurrentLookupKey() {
return t.get();
}
//数据源加载完毕后立即存入map中,准备数据源路由的操作
@Override
public void afterPropertiesSet() {
//设置数据源集合
HashMap<Object, Object> map = new HashMap<>();
map.put("ds1", ds1);
map.put("ds2", ds2);
super.setTargetDataSources(map);
//设置默认数据源
super.setDefaultTargetDataSource(ds1);
super.afterPropertiesSet();
}
}
创建一个controller包,包下创建一个UserController .java类,注入DyDatasource .java类切换不同的数据源.
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@Autowired
private DyDatasource dyDatasource;
@GetMapping("/select")
public List<User> select(){
//耦合度过高
dyDatasource.t.set("ds1");
List<User> users = userService.selectUser();
return users;
}
@GetMapping("/insert")
public String insertUser(){
//耦合度过高
dyDatasource.t.set("ds2");
User user = new User();
user.setUsername("aaa");
user.setPassword("1234");
userService.insertUser(user);
return "ok";
}
}
创建一个service包,包下创建UserService.java类
public interface UserService {
List<User> selectUser();
void insertUser(User user);
}
创建一个service.impl包,包下创建UserServiceImpl.java类
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserMapper userMapper;
@Override
@WR("ds1")//使用哪个数据源就用这个注解引用;
public List<User> selectUser() {
return userMapper.selectByExample(null);
}
@Override
@WR("ds2")
public void insertUser(User user) {
userMapper.insertSelective(user);
}
}