MyBatis Plus动态数据源
- 简述
- 工程搭建
- 1. 创建Maven工程
- 2. 依赖导入
- 3. 编写配置文件
- 4. 数据源
- 5. 选择数据源注解
- 6. 编写AOP切面
- 5. 编写测试类
简述
Mybatis plus动态获取数据源,获取不同数据库连接,这篇文章编写是通过一个注解指定方法去获取那个数据。
工程搭建
1. 创建Maven工程
2. 依赖导入
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- 多数据源依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
</dependencies>
父类依赖导入的版本如下
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath/>
</parent>
3. 编写配置文件
application.yml
server:
port: 8080
spring:
application:
name: mwb
mybatis-plus:
mapper-locations: classpath:mapper/*.xml
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
数据库连接配置文件:db.properties
spring.datasource.configLocationPath=mybatis-config.xml
# master
spring.datasource.master.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.master.jdbc-url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false
spring.datasource.master.username=root
spring.datasource.master.password=数据库密码
# master 配置
spring.datasource.master.aliasesPackagePath=com.mai.bin.entity.master
spring.datasource.master.mapperPath=classpath:mapper/master/*.xml
# slave
spring.datasource.slave.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.slave.jdbc-url=jdbc:mysql://localhost:3306/ego?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false
spring.datasource.slave.username=root
spring.datasource.slave.password=数据库密码
# slave 配置
spring.datasource.slave.aliasesPackagePath=com.mai.bin.entity.slave
spring.datasource.slave.mapperPath=classpath:mapper/slave/*.xml
4. 数据源
创建DataSourceConfig配置类
@Configuration
@PropertySource("classpath:db.properties")
public class DataSourceConfig {
/**
* 数据源 一
*/
@Bean("master")
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSource masterDataSource(){
return DataSourceBuilder.create().build();
}
/**
* 数据源 二
*/
@Bean("slave")
@ConfigurationProperties(prefix = "spring.datasource.slave")
public DataSource slaveDataSource(){
return DataSourceBuilder.create().build();
}
/**
* 主数据源
*/
@Primary
@Bean
public MyDynamicDataSource dataSource(
@Qualifier("master") DataSource master,
@Qualifier("slave") DataSource slave){
HashMap<String, DataSource> targetDataSource = new HashMap<>();
targetDataSource.put("master", master);
targetDataSource.put("slave", slave);
return new MyDynamicDataSource(master, targetDataSource);
}
}
创建自定动态数据源MyDynamicDataSource
public class MyDynamicDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<String> source = new InheritableThreadLocal<>();
public MyDynamicDataSource(DataSource defaultDataSource, Map<String, DataSource> targetDataSources){
super.setDefaultTargetDataSource(defaultDataSource);
super.setTargetDataSources(new HashMap<>(targetDataSources));
}
/**
* 返回当前dataSourceKey值
*/
@Override
protected Object determineCurrentLookupKey() {
return source.get();
}
// 设置 dataSourceKey 的值
public static void setDataSource(String dataSource){
source.set(dataSource);
}
// 清除 dataSourceKey 的值
public static void toDefault(){
source.remove();
}
}
动态数据源的获取需要继承AbstractRoutingDataSource类,重写determineCurrentLookupKey方法获取当前数据连接,这里用到ThreadLocal类,保证线程安全。
5. 选择数据源注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface ChoiceDataSource {
// 默认选择主数据源
String value() default "master";
}
6. 编写AOP切面
@Aspect
@Component
@Order(-1)
public class DataSourceAspect {
@Pointcut("@annotation(com.mai.bin.annotation.ChoiceDataSource)")
// 这个注解是意思是controller下所有方法都切入
// @Pointcut("execution(* com.mai.bin.controller..*.*(..))")
public void choiceDataSource(){
}
@Around(value = "choiceDataSource()")
public Object choiceDataSourceAround(ProceedingJoinPoint point){
try {
// 获取方法
Method method = ((MethodSignature) point.getSignature()).getMethod();
// 检查方法上是否存在选择数据源的注解,没有就主数据源
ChoiceDataSource choiceDataSource = method.getAnnotation(ChoiceDataSource.class);
System.out.println("choiceDataSource: " + choiceDataSource);
if (ObjectUtil.isNotEmpty(choiceDataSource)){
System.out.println("进入choiceDataSource....");
// 设置注解里面的数据源
MyDynamicDataSource.setDataSource(choiceDataSource.value());
System.out.println("dataSource " + choiceDataSource.value());
}
// 运行目标方法
Object result = point.proceed();
return result;
} catch (Throwable e) {
e.printStackTrace();
}finally {
// ThreadLocal 需要释放资源
MyDynamicDataSource.toDefault();
}
return null;
}
}
上面代码意思就是创建切面,同过注解方法实现,环绕通知跟反射确定方法中是否存在选择数据源注解,注解value有值,则进行选择,否则默认为主数据源。
注意: 记得进行资源释放,不然ThreadLocal会造成内存泄漏。
5. 编写测试类
entity、mapper层这里忽略过,controller层如下
==注意:==UserController对应是slave进行测试,TestIndexController对应是master进行测试。
UserController
@RestController
public class UserController {
@Autowired
@Qualifier("userMapper")
private UserMapper userMapper;
@ChoiceDataSource("slave")
@RequestMapping("user/{id}")
public String user(@PathVariable("id") Integer id){
return userMapper.selectByIdUser(id).toString();
}
}
TestIndexController
@RestController
public class TestIndexController {
@Autowired
@Qualifier("testIndexMapper")
private TestIndexMapper testIndexMapper;
@ChoiceDataSource("master")
@RequestMapping("/testIndex/{id}")
public String testIndex(@PathVariable("id") Integer id){
return testIndexMapper.selectAllById1(id).toString();
}
/**
* 测试默认值
*/
@ChoiceDataSource()
@RequestMapping("/testIndex2/{id}")
public String testIndex2(@PathVariable("id") Integer id){
return testIndexMapper.selectAllById1(id).toString();
}
}
编写主启动类
需要去除自动装配数据源
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@MapperScan({"com.mai.bin.mapper"})
public class Test08Main {
public static void main(String[] args) {
SpringApplication.run(Test08Main.class, args);
}
}
测试结果
有错误或者更好的实现方法,欢迎交流!!!