SpringBoot多数据源基于mybatis插件(三)
- 1.主要思路
- 2.具体实现
1.主要思路
MyBatis
的插件机制允许你在MyBatis
的四大对象(Executor、StatementHandler、ParameterHandler和ResultSetHandler
)的方法执行前后进行拦截,并可以在方法执行前后插入自定义逻辑。
这里其实就是利用Exector
对象执行多数据操作,通过判断操作的类型来动态选择数据源。
2.具体实现
以下是一个Springboot
整合mybatis的
案例:
yml
文件配置:
spring:
datasource:
datasource1:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/test_master
username: root
password: root
druid:
initial-size: 1
min-idle: 1
max-active: 20
test-on-borrow: true
datasource2:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/test_slave
username: root
password: root
druid:
initial-size: 1
min-idle: 1
max-active: 20
test-on-borrow: true
mybatis:
type-aliases-package: com.rql.entity
mapper-locations: classpath:mybatis/*.xml
DataSourceConfig.java
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.datasource1")
public DataSource dataSource1(){
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.datasource2")
public DataSource dataSource2(){
return DataSourceBuilder.create().build();
}
@Bean
public Interceptor DynamicDataSourcePlugin(){
return new DynamicDataSourcePlugin();
}
}
DynamicDataSourcePlugin.java
@Intercepts({@Signature(type = Executor.class,method = "update",args = {MappedStatement.class,Object.class}),
@Signature(type = Executor.class,method = "query",args = {MappedStatement.class,Object.class, RowBounds.class,
ResultHandler.class})})
public class DynamicDataSourcePlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
//拿到当前方法【update、query】的所有参数
Object[] objects = invocation.getArgs();
//MappedStatement 封装sql
MappedStatement ms = (MappedStatement) objects[0];
//读方法
if (ms.getSqlCommandType().equals(SqlCommandType.SELECT)) {
DynamicDataSource.name.set("r");
} else {//写方法
DynamicDataSource.name.set("w");
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
if (target instanceof Executor) {
return Plugin.wrap(target, this);
} else {
return target;
}
}
@Override
public void setProperties(Properties properties) {
}
}
@Intercepts
注解:
@Intercepts
:注解用于指定该插件要拦截的目标方法和参数。
@Signature
:注解用于指定要拦截的接口、方法及其参数类型。在这个例子中,插件拦截了 Executor
接口的 update
和 query
方法。
intercept
方法:
这是插件的核心方法,当被拦截的方法被调用时,这个方法会被执行。
通过 Invocation
对象,可以获取到被拦截方法的参数,并可以执行被拦截的方法。
在这里,根据SQL
的类型(读或写),使用 DynamicDataSource.name.set()
方法来设置不同的数据源(假设 DynamicDataSource
这里设置为 “r” 代表读数据源,“w” 代表写数据源)。
DynamicDataSource.java
@Component
@Primary
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初始化数据源
HashMap<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("w", dataSource1);
targetDataSources.put("r", dataSource2);
super.setTargetDataSources(targetDataSources);
//为defaultTargetDataSource设置默认数据源
super.setDefaultTargetDataSource(dataSource1);
super.afterPropertiesSet();
}
}
同时,需要将DynamicDataSourcePlugin 的Bean注入到Spring容器中
@Bean
public Interceptor DynamicDataSourcePlugin(){
return new DynamicDataSourcePlugin();
}