场景
因为引用了baomidou主从数据源,因为业务场景特殊,需要查询语句强制走主库,把解决方案分享出来,帮助大家少走弯路
pom依赖
java
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.6.1</version>
</dependency>
主从配置
java
package com.baomidou.config;
import com.baomidou.dynamic.datasource.plugin.MasterSlaveAutoRoutingPlugin;
import com.baomidou.interceptor.MasterSlaveAutoRoutingOrSelectByRulePlugin;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 主从配置
*/
@Configuration
public class MasterSlaveAutoRoutingPluginConfig {
/**
* xml文件sql语句标签名包含'FromMaster' 会去主库查询
*/
@Bean
public MasterSlaveAutoRoutingOrSelectByRulePlugin masterSlaveAutoRoutingPlugin(){
return new MasterSlaveAutoRoutingOrSelectByRulePlugin();
}
}
拦截器
java
package com.baomidou.interceptor;
import com.baomidou.dynamic.datasource.support.DdConstants;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.Properties;
/**
* 根据 baomidou MasterSlaveAutoRoutingPlugin 修改符合自己的调用规则(目前根据sql方法的唯一id来判断)
* 自动切换主从或者根据方法名指定路由
*/
@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 MasterSlaveAutoRoutingOrSelectByRulePlugin implements Interceptor {
/**
* xml文件sql语句标签名包含'FromMaster' 会去主库查询
* 如 '<select id="find***FromMaster" ...>'
*/
public static final String FROM_MASTER = "FromMaster";
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object[] args = invocation.getArgs();
MappedStatement ms = (MappedStatement) args[0];
String pushedDataSource = null;
try {
if (ms.getId().contains(MasterSlaveAutoRoutingOrSelectByRulePlugin.FROM_MASTER_HT) && SqlCommandType.SELECT == ms.getSqlCommandType()) {
pushedDataSource = DynamicDataSourceContextHolder.push(DdConstants.MASTER);
return invocation.proceed();
}
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) {
}
}
dao
java
package com.baomidou.mapper;
import com.baomidou.entity.TOrder;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* Mapper 接口
* </p>
*/
public interface TOrderMapper extends BaseMapper<TOrder> {
TOrder findOrderFromMaster();
}
xml
java
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.baomidou.mapper.TOrderMapper">
<select id="findOrderFromMaster" resultType="com.baomidou.entity.TOrder">
-- 根据名称使sql强制走主库
</select>
</mapper>
Yml
java
server:
port: 9914
servlet:
context-path: /baomidou
logging:
level:
com.baomidou.dynamic: debug
spring:
datasource:
dynamic:
primary: master # 这里需要修改
strict: true
hikari:
pool-name: Yi_HikariCP
minimum-idle: 5
maximum-pool-size: 20
datasource:
# 测试
master:
type: com.zaxxer.hikari.HikariDataSource
url: jdbc:mysql://localhost:3396/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8
username: admin
password: ufcz2b8x3bas4c5m$%332
driver-class-name: com.mysql.cj.jdbc.Driver
slave_1:
type: com.zaxxer.hikari.HikariDataSource
url: jdbc:mysql://localhost:3397/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8
username: readonly
password: 123qwe!@#
driver-class-name: com.mysql.cj.jdbc.Driver
slave_2:
type: com.zaxxer.hikari.HikariDataSource
url: jdbc:mysql://localhost:3397/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8
username: readonly
password: 123qwe!@#
driver-class-name: com.mysql.cj.jdbc.Driver
#seata事务生效
seata: true
seata-mode: at
seata:
enabled: true
application-id: baomidou-seata
#不使用自动代理数据源
enable-auto-data-source-proxy: false
mybatis-plus:
#================================================= mybatis-plus start =================================================
# classpath:/mapper/*Mapper.xml
mapper-locations: classpath*:/mapper/*.xml
# 实体扫描,多个package用逗号或者分号分隔
type-aliases-package: com.user.entity
configuration:
# 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 驼峰下划线转换
map-underscore-to-camel-case: true
cache-enabled: false
# 如果查询结果中包含空值的列,则 MyBatis 在映射的时候,不会映射这个字段
call-setters-on-nulls: false
global-config:
# 刷新mapper 调试神器
refresh: true
banner: false
#数据库大写下划线转换
#capital-mode: true
#序列接口实现类配置
#key-generator: com.baomidou.springboot.xxx
# 数据库相关配置
db-config:
db-type: mysql
# 主键类型 AUTO:"数据库ID自增", INPUT:"用户输入ID",ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";
id-type: UUID
# 字段策略 IGNORED:"忽略判断",NOT_NULL:"非 NULL 判断"),NOT_EMPTY:"非空判断"
field-strategy: NOT_EMPTY
capital-mode: true
#逻辑删除配置
logic-delete-value: 1
logic-not-delete-value: 0
#================================================= mybatis-plus end ===================================================