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.Driverjdbc-url: jdbc:mysql://localhost:3306/test_masterusername: rootpassword: rootdruid:initial-size: 1min-idle: 1max-active: 20test-on-borrow: truedatasource2:driver-class-name: com.mysql.cj.jdbc.Driverjdbc-url: jdbc:mysql://localhost:3306/test_slaveusername: rootpassword: rootdruid:initial-size: 1min-idle: 1max-active: 20test-on-borrow: truemybatis:type-aliases-package: com.rql.entitymapper-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();}@Beanpublic 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 {@Overridepublic Object intercept(Invocation invocation) throws Throwable {//拿到当前方法【update、query】的所有参数Object[] objects = invocation.getArgs();//MappedStatement 封装sqlMappedStatement ms = (MappedStatement) objects[0];//读方法if (ms.getSqlCommandType().equals(SqlCommandType.SELECT)) {DynamicDataSource.name.set("r");} else {//写方法DynamicDataSource.name.set("w");}return invocation.proceed();}@Overridepublic Object plugin(Object target) {if (target instanceof Executor) {return Plugin.wrap(target, this);} else {return target;}}@Overridepublic 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<>();@AutowiredDataSource dataSource1;@AutowiredDataSource dataSource2;@Overrideprotected Object determineCurrentLookupKey() {return name.get();}@Overridepublic 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容器中
@Beanpublic Interceptor DynamicDataSourcePlugin(){return new DynamicDataSourcePlugin();}