想必大家在论坛上已经看过很多使用苞米豆的dynamic做读写分离的方法,依赖如下:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>${dynamic-ds.version}</version>
</dependency>
版本3.5.2
内置的DS注解在注解上添加@DS("datasourceName")做读写分离,如果在项目早期,添加这个注解确实也是挺方便的,但是当项目快要完工了 代码量很多,一个个加上@DS注解会累死人的,所以我们可以在项目中创建一个类也可以不用,其主要原理是mybatis-plus框架的com.baomidou.dynamic.datasource.plugin包里的MasterSlaveAutoRoutingPlugin
@Configuration
public class MybatisPlusConfig {
//mybatis-plus分页插件
@Bean
public PaginationInnerInterceptor paginationInnerInterceptor(){
return new PaginationInnerInterceptor(DbType.MYSQL);
}
//mybatis-plus读写分离插件
@Bean
public MasterSlaveAutoRoutingPlugin masterSlaveAutoRoutingPlugin(){
return new MyMasterSlaveAutoRoutingPlugin();
}
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(paginationInnerInterceptor()); //注意使用哪种数据库
return interceptor;
}
}
这里的MasterSlaveAutoRoutingPlugin就是实现读写分离的插件,这里我继承了MasterSlaveAutoRoutingPlugin写了一个MyMasterSlaveAutoRoutingPlugin 其实没有改什么东西,只是加了一个lambok的log输出,MyMasterSlaveAutoRoutingPlugin代码如下:
@Slf4j
@Intercepts({@Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
), @Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}
), @Signature(
type = Executor.class,
method = "update",
args = {MappedStatement.class, Object.class}
)})
public class MyMasterSlaveAutoRoutingPlugin extends MasterSlaveAutoRoutingPlugin {
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object[] args = invocation.getArgs();
MappedStatement ms = (MappedStatement)args[0];
String pushedDataSource = null;
Object var6;
try {
String dataSource = SqlCommandType.SELECT == ms.getSqlCommandType() ? "slave" : "master";
log.info("===========================正在查询[{}]数据库,{}语句========================",dataSource,ms.getSqlCommandType());
pushedDataSource = DynamicDataSourceContextHolder.push(dataSource);
var6 = invocation.proceed();
} finally {
if (pushedDataSource != null) {
DynamicDataSourceContextHolder.poll();
}
}
return var6;
}
}
MasterSlaveAutoRoutingPlugin代码:
@Intercepts({
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
@Slf4j
public class MasterSlaveAutoRoutingPlugin implements Interceptor {
@Autowired
protected DataSource dynamicDataSource;
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object[] args = invocation.getArgs();
MappedStatement ms = (MappedStatement) args[0];
String pushedDataSource = null;
try {
String dataSource = SqlCommandType.SELECT == ms.getSqlCommandType() ? DdConstants.SLAVE : DdConstants.MASTER;
pushedDataSource = DynamicDataSourceContextHolder.push(dataSource);
return invocation.proceed();
} finally {
if (pushedDataSource != null) {
DynamicDataSourceContextHolder.poll();
}
}
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
其中跟读写分离最重要的就是intercept方法,其原理就是判断mybaits-plus所执行的SQL语句是否是SELECT语句 如果是SELECT语句 则走从库,如果是其它语句 则走主库,从而实现读写分离,无需使用@DS注解,方便许多
让我们来看看最终的效果:
读从库:
写主库: