第一步、导包
数据源使用druid,这里druid和mybatis-plus版本不是最新,可自行选择。
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>2.1.9</version>
</dependency>
第二步、写配置
这里数据库使用的SqlServer,自行修改,oracle或mysql见jdbc快速入坑(一)
- application.yml配置如下:
# Tomcat
server:
tomcat:
uri-encoding: UTF-8
max-threads: 1000
min-spare-threads: 30
port: 8088
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
druid:
stat-view-servlet:
enabled: true
url-pattern: /druid/*
loginUsername: admin
loginPassword: 123456
dynamic:
primary: master
health: true
druid:
initial-size: 10
max-active: 100
min-idle: 10
max-wait: 60000
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
test-while-idle: true
test-on-borrow: false
test-on-return: false
validationQuery: "select 1"
filters: stat,wall
stat:
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: false
wall:
multi-statement-allow: true
datasource:
master: #数据源1
url: jdbc:sqlserver://127.0.0.1:1051;databaseName=test
username: ENC(L36gh72XwqNKz00VQnxz9XhApDcpf5Q4oaW5nwbpE4e1TBGL81HxQcfI64eBXMiSTwI8+f70hW50aX2iZdFyBw==)
password: ENC(f4j9JTNqd7XkuSlDLM1od6AByM8zw/crcd4lo47l97JIWD62VvBXJK5EFBWV67We4wThc9r0oIc8tKdfsZbO5g==)
slave:
url: jdbc:sqlserver://121.196.190.248:1050;databaseName=test
username: ENC(L36gh72XwqNKz00VQnxz9XhApDcpf5Q4oaW5nwbpE4e1TBGL81HxQcfI64eBXMiSTwI8+f70hW50aX2iZdFyBw==)
password: ENC(f4j9JTNqd7XkuSlDLM1od6AByM8zw/crcd4lo4Ml97JIWD62VvBXJK5EFBWV67We4wThc9r0oIc8tKdfsZbO5g==)
mybatis-plus:
mapper-locations: classpath*:mapper/**/*.xml
#实体扫描,多个package用逗号或者分号分隔
typeAliasesPackage: com.test.*.entity
global-config:
#主键类型 0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
id-type: 0
#字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
field-strategy: 1
#驼峰下划线转换
db-column-underline: true
#刷新mapper 调试神器
refresh-mapper: true
#数据库大写下划线转换
#capital-mode: true
# Sequence序列接口实现类配置
#key-generator: com.baomidou.mybatisplus.incrementer.OracleKeyGenerator
#逻辑删除配置
logic-delete-value: -1
logic-not-delete-value: 0
#自定义填充策略接口实现
#meta-object-handler: com.baomidou.springboot.xxx
#自定义SQL注入器
sql-injector: com.baomidou.mybatisplus.mapper.LogicSqlInjector
configuration:
map-underscore-to-camel-case: true
cache-enabled: false
call-setters-on-nulls: true
#打印mybatis的执行sql
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
- 重写读写分离插件配置
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
import com.baomidou.dynamic.datasource.support.DbHealthIndicator;
import com.baomidou.dynamic.datasource.support.DdConstants;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import java.util.Properties;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
@Intercepts({
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
@Slf4j
public class MasterSlaveAutoRoutingPlugin implements Interceptor {
@Autowired
private DynamicDataSourceProperties properties;
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object[] args = invocation.getArgs();
MappedStatement ms = (MappedStatement) args[0];
try {
if (StringUtils.isEmpty(DynamicDataSourceContextHolder.peek())){
String source = SqlCommandType.SELECT == ms.getSqlCommandType() ? DdConstants.SLAVE : DdConstants.MASTER;
DynamicDataSourceContextHolder.push(source);
}
return invocation.proceed();
} finally {
DynamicDataSourceContextHolder.poll();
}
}
/**
* 获取动态数据源名称,重写注入 DbHealthIndicator 支持数据源健康状况判断选择
*
* @param mappedStatement mybatis MappedStatement
* @return 获取真实的数据源名称
*/
public String getDataSource(MappedStatement mappedStatement) {
String slave = DdConstants.SLAVE;
if (properties.isHealth()) {
/*
* 根据从库健康状况,判断是否切到主库
*/
boolean health = DbHealthIndicator.getDbHealth(DdConstants.SLAVE);
if (!health) {
health = DbHealthIndicator.getDbHealth(DdConstants.MASTER);
if (health) {
slave = DdConstants.MASTER;
}
}
}
return SqlCommandType.SELECT == mappedStatement.getSqlCommandType() ? slave : DdConstants.MASTER;
}
@Override
public Object plugin(Object target) {
return target instanceof Executor ? Plugin.wrap(target, this) : target;
}
@Override
public void setProperties(Properties properties) {
}
}
- 新建MybaitsPlus配置类MybatisPlusConfig
import com.baomidou.mybatisplus.plugins.PaginationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MybatisPlusConfig {
/**
* 分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
/**
* 读写分离插件
*/
@Bean
public MasterSlaveAutoRoutingPlugin masterSlaveAutoRoutingPlugin(){
return new MasterSlaveAutoRoutingPlugin();
}
}
- 定义数据源常量
public interface DBConstants {
String MASTER = "master";
String SLAVE_GROUP = "slave";
String SLAVE1 = "slave_1";
}
第三步、测试
- 测试代码
@RequestMapping("/test")
@RestController
public class TestController {
@Autowired
private AreaService areaService;
/**
* 使用主库查询
*/
@GetMapping
@DS(DBConstants.MASTER)
public Object test(){
Wrapper<AreaEntity> wrapper = new EntityWrapper<>();
return areaService.selectList(wrapper);
}
/**
* 未加@DS注解,查询操作默认使用slave从库,插入操作默认使用master主库
*/
@GetMapping("/11")
public Object test11(){
Wrapper<AreaEntity> wrapper = new EntityWrapper<>();
return areaService.selectList(wrapper);
}
/**
*使用从库查询
*/
@GetMapping("/22")
@DS(DBConstants.SLAVE_GROUP)
public Object test22(){
Wrapper<AreaEntity> wrapper = new EntityWrapper<>();
return areaService.selectList(wrapper);
}
}
当未加@DS注解时,查询操作默认使用从库,修改删除和添加操作默认使用主库,如果添加@DS注解,则以注解为准。