1.逻辑删除
说明:
只对自动注入的sql起效:
-
插入: 不作限制
-
查找: 追加where条件过滤掉已删除数据,且使用 wrapper.entity 生成的where条件会忽略该字段
-
更新: 追加where条件防止更新到已删除数据,且使用 wrapper.entity 生成的where条件会忽略该字段
-
删除: 转变为 更新
例如: -
删除:
update user set deleted=1 where id = 1 and deleted=0
-
查找:
select id,name,deleted from user where deleted=0
字段类型支持说明:
- 支持所有数据类型(推荐使用
Integer
,Boolean
,LocalDateTime
) - 如果数据库字段使用datetime,逻辑未删除值和已删除值支持配置为字符串null,另一个值支持配置为函数来获取值如now()
附录:
- 逻辑删除是为了方便数据恢复和保护数据本身价值等等的一种方案,但实际就是删除。
- 如果你需要频繁查出来看就不应使用逻辑删除,而是以一个状态去表示。
逻辑删除的使用步骤
在 User
实体类中增加 deleted
字段,并用 @TableLogic
注解标识这是一个逻辑删除字段
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("t_user")
public class User extends Model<User> {
@TableId(type = IdType.AUTO)
private Long id;
@TableField("username")
private String name;
private Integer age;
private String email;
@Version
private Integer version;
@TableLogic
private Integer deleted;
}
在 t_user
表中添加 deleted
列,对于逻辑删除列,最好都添加上一个默认值(逻辑未删除的值)
在 application.yml 配置文件中设置 logic-delete-value(逻辑已删除值)和 logic-not-delete-value(逻辑未删除值)
步骤1: 配置
mybatis-plus:
global-config:
db-config:
logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
步骤2: 实体类字段上加上@TableLogic注解
@TableLogic
private Integer deleted;
测试代码
@Test
public void testLogicDelete() {
int count = userMapper.deleteById(1);
System.out.println("影响行数:" + count);
}
从 SQL 日志可以看到,删除操作变成了更新操作:UPDATE t_user SET deleted=1 WHERE id=? AND deleted=0
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3c87e6b7] was not registered for synchronization because synchronization is not active
2021-04-25 08:21:52.228 INFO 9828 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2021-04-25 08:21:52.388 INFO 9828 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
JDBC Connection [HikariProxyConnection@1069163325 wrapping com.mysql.cj.jdbc.ConnectionImpl@427ae189] will not be managed by Spring
==> Preparing: UPDATE t_user SET deleted=1 WHERE id=? AND deleted=0
==> Parameters: 1(Integer)
<== Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3c87e6b7]
影响行数:1
逻辑删除的原理
我们在 application.yml
配置文件中设置的值会绑定到 GlobalConfig
全局配置类的 logicDeleteField
、logicDeleteValue
、logicNotDeleteValue
字段中
/**
* Mybatis 全局缓存
*
* @author Caratacus
* @since 2016-12-06
*/
@Data
@Accessors(chain = true)
@SuppressWarnings("serial")
public class GlobalConfig implements Serializable {
/**
* 逻辑删除全局属性名
*/
private String logicDeleteField;
/**
* 逻辑删除全局值(默认 1、表示已删除)
*/
private String logicDeleteValue = "1";
/**
* 逻辑未删除全局值(默认 0、表示未删除)
*/
private String logicNotDeleteValue = "0";
TableFieldInfo
类对应于数据库表的字段信息:logicDelete
表示该字段是否为逻辑删除字段;logicDeleteValue
为逻辑删除值;logicNotDeleteValue
逻辑未删除值
/**
* 数据库表字段反射信息
*
* @author hubin sjy willenfoo tantan
* @since 2016-09-09
*/
@Getter
@ToString
@EqualsAndHashCode
@SuppressWarnings("serial")
public class TableFieldInfo implements Constants {
/**
* 是否是逻辑删除字段
*/
private boolean logicDelete = false;
/**
* 逻辑删除值
*/
private String logicDeleteValue;
/**
* 逻辑未删除值
*/
private String logicNotDeleteValue;
/**
* 逻辑删除初始化
*
* @param dbConfig 数据库全局配置
* @param field 字段属性对象
*/
private void initLogicDelete(GlobalConfig.DbConfig dbConfig, Field field, boolean existTableLogic) {
/* 获取注解属性,逻辑处理字段 */
TableLogic tableLogic = field.getAnnotation(TableLogic.class);
if (null != tableLogic) {
if (StringUtils.isNotBlank(tableLogic.value())) {
this.logicNotDeleteValue = tableLogic.value();
} else {
this.logicNotDeleteValue = dbConfig.getLogicNotDeleteValue();
}
if (StringUtils.isNotBlank(tableLogic.delval())) {
this.logicDeleteValue = tableLogic.delval();
} else {
this.logicDeleteValue = dbConfig.getLogicDeleteValue();
}
this.logicDelete = true;
} else if (!existTableLogic) {
String deleteField = dbConfig.getLogicDeleteField();
if (StringUtils.isNotBlank(deleteField) && this.property.equals(deleteField)) {
this.logicNotDeleteValue = dbConfig.getLogicNotDeleteValue();
this.logicDeleteValue = dbConfig.getLogicDeleteValue();
this.logicDelete = true;
}
}
}
我们来看看 DeleteById
类中注入 SQL 语句的逻辑
如果if (tableInfo.isWithLogicDelete())
成立,即表中有逻辑删除字段,MybatisPlus 会将删除操作变为更新操作:addUpdateMappedStatement(mapperClass, modelClass, getMethod(sqlMethod), sqlSource)
否则if (tableInfo.isWithLogicDelete())
成立,即表中没有逻辑删除字段,MybatisPlus 会注入删除操作的 SQL 语句:this.addDeleteMappedStatement(mapperClass, getMethod(sqlMethod), sqlSource)
/**
* 根据 ID 删除
*/
public class DeleteById extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
String sql;
SqlMethod sqlMethod = SqlMethod.LOGIC_DELETE_BY_ID;
if (tableInfo.isWithLogicDelete()) {
sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), sqlLogicSet(tableInfo),
tableInfo.getKeyColumn(), tableInfo.getKeyProperty(),
tableInfo.getLogicDeleteSql(true, true));
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, Object.class);
return addUpdateMappedStatement(mapperClass, modelClass, getMethod(sqlMethod), sqlSource);
} else {
sqlMethod = SqlMethod.DELETE_BY_ID;
sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), tableInfo.getKeyColumn(),
tableInfo.getKeyProperty());
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, Object.class);
return this.addDeleteMappedStatement(mapperClass, getMethod(sqlMethod), sqlSource);
}
}
}
UserMapper#deleteById()
方法注入的 SQL 语句为 "UPDATE t user SET deleted=1 WHEREid=? AND deleted=0
,删除操作变为对逻辑字段的更新操作
常见问题:
1.如何 insert ?
- 字段在数据库定义默认值(推荐)
- insert 前自己 set 值
- 使用自动填充功能
2.删除接口自动填充功能失效
使用 update
方法并: UpdateWrapper.set(column, value)(推荐)
使用 update
方法并: UpdateWrapper.setSql("column=value")
使用Sql注入器注入com.baomidou.mybatisplus.extension.injector.methods.LogicDeleteByIdWithFill并使用(推荐)
2.通用枚举
在一些情况下,我们希望实体的字段是固定的几个值,比如:用户表的性别(Gender),我们希望是固定的Male,Female 我们可能在设计数据库字段时,采用了int(1):
- 1: 男
- 2: 女
如果实体使用了Integer,那么无法保证新增时传入的参数是1/2
通用枚举就是为了解决这样的场景
1.首先我们定义一个枚举类型
public enum GenderEnum {
MALE(1, "男"),
FEMALE(2, "女");
private final int code;
private final String descp;
//。。。省略其他
}
2.定义用户实体时,使用枚举类型
public class User {
private String name;
/**
* 使用枚举类型来限制输入值
*/
private GenderEnum gender;
}
这样是不是就好了呢?
- 答案是NO
mybatis原生默认是以枚举的名称: Enum.name()作为默认值
String val = GenderEnum.FEMALE.name();
也就是说,对应数据库的字段必须是一个varchar类型。这显然违背了我们的初衷。
Mybatis-Plus的通用枚举处理,增强了原生的枚举处理,让匹配的数据可以自由定制。
你只需要极少的配置即可:
解决了繁琐的配置,让 mybatis 优雅的使用枚举属性!
自3.1.0
开始,如果你无需使用原生枚举,可配置默认枚举来省略扫描通用枚举配置 默认枚举配置
-
升级说明:
3.1.0
以下版本改变了原生默认行为,升级时请将默认枚举设置为EnumOrdinalTypeHandler
-
影响用户:
实体中使用原生枚举
-
其他说明:
配置枚举包扫描的时候能提前注册使用注解枚举的缓存
声明通用枚举属性
1.配置扫描枚举类的路径
mybatis-plus:
type-enums-package: com.baomidou.mybatisplus.samples.enums.enums
mybatis-plus.configuration.default-enum-type-handler=org.apache.ibatis.type.EnumOrdinalTypeHandler
2.声明枚举属性
方式一: 使用 @EnumValue 注解枚举属性
public enum GradeEnum {
PRIMARY(1, "小学"),
SECONDORY(2, "中学"),
HIGH(3, "高中");
private int code;
@EnumValue//描述作为枚举值保存到数据库
private String desc;
GradeEnum(int code, String desc) {
this.code = code;
this.desc = desc;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
通过@EnumValue注解,告诉Mybatis-Plus, 枚举类的真正的value是什么,就实现了自由定制。
方式二: 枚举属性,实现 IEnum 接口如下:
public enum AgeEnum implements IEnum<Integer> {
ONE(1, "一岁"),
TWO(2, "二岁"),
THREE(3, "三岁");
private int value;
private String desc;
AgeEnum(final int value, final String desc) {
this.value = value;
this.desc = desc;
}
@Override
public Integer getValue() {
return value;
}
}
通过getValue()方法,告诉Mybatis-Plus,枚举类的真正的value是什么,这样就实现了自由定制。
如何序列化枚举值为数据库存储值?
Jackson
一、重写toString方法
springboot
@Bean
public Jackson2ObjectMapperBuilderCustomizer customizer(){
return builder -> builder.featuresToEnable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
}
jackson
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
以上两种方式任选其一,然后在枚举中复写toString方法即可.
二、注解处理
public enum GradeEnum {
PRIMARY(1, "小学"), SECONDORY(2, "中学"), HIGH(3, "高中");
GradeEnum(int code, String descp) {
this.code = code;
this.descp = descp;
}
@EnumValue
@JsonValue //标记响应json值
private final int code;
}
Fastjson
一、重写toString方法
全局处理方式
FastJsonConfig config = new FastJsonConfig();
config.setSerializerFeatures(SerializerFeature.WriteEnumUsingToString);
局部处理方式
@JSONField(serialzeFeatures= SerializerFeature.WriteEnumUsingToString)
private UserStatus status;
以上两种方式任选其一,然后在枚举中复写toString方法即可.
3.字段类型处理器
类型处理器,用于 JavaType 与 JdbcType 之间的转换,用于 PreparedStatement 设置参数值和从 ResultSet 或 CallableStatement 中取出一个值,本文讲解 mybaits-plus
内置常用类型处理器如何通过TableField
注解快速注入到 mybatis
容器中。
- JSON 字段类型
@Data
@Accessors(chain = true)
@TableName(autoResultMap = true)
public class User {
private Long id;
...
/**
* 注意!! 必须开启映射注解
*
* @TableName(autoResultMap = true)
*
* 以下两种类型处理器,二选一 也可以同时存在
*
* 注意!!选择对应的 JSON 处理器也必须存在对应 JSON 解析依赖包
*/
@TableField(typeHandler = JacksonTypeHandler.class)
// @TableField(typeHandler = FastjsonTypeHandler.class)
private OtherInfo otherInfo;
}
该注解对应了 XML 中写法为
<result column="other_info" jdbcType="VARCHAR" property="otherInfo" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler" />
4.自动填充功能
原理:
-
实现元对象处理器接口:
com.baomidou.mybatisplus.core.handlers.MetaObjectHandler
-
注解填充字段
@TableField(.. fill = FieldFill.INSERT)
生成器策略部分也可以配置!
public class User {
// 注意!这里需要标记为填充字段
@TableField(.. fill = FieldFill.INSERT)
private String fillField;
....
}
- 自定义实现类 MyMetaObjectHandler
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ....");
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用)
// 或者
this.strictInsertFill(metaObject, "createTime", () -> LocalDateTime.now(), LocalDateTime.class); // 起始版本 3.3.3(推荐)
// 或者
this.fillStrategy(metaObject, "createTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug)
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ....");
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐)
// 或者
this.strictUpdateFill(metaObject, "updateTime", () -> LocalDateTime.now(), LocalDateTime.class); // 起始版本 3.3.3(推荐)
// 或者
this.fillStrategy(metaObject, "updateTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug)
}
}
注意事项:
- 填充原理是直接给entity的属性设置值!!!
- 注解则是指定该属性在对应情况下必有值,如果无值则入库会是null
- MetaObjectHandler提供的默认方法的策略均为:如果属性有值则不覆盖,如果填充值为null则不填充
- 字段必须声明TableField注解,属性fill选择对应策略,该声明告知Mybatis-Plus需要预留注入SQL字段
- 填充处理器MyMetaObjectHandler在 Spring Boot 中需要声明@Component或@Bean注入
- 要想根据注解FieldFill.xxx和字段名以及字段类型来区分必须使用父类的strictInsertFill或者strictUpdateFill方法
- 不需要根据任何来区分可以使用父类的fillStrategy方法
public enum FieldFill {
/**
* 默认不处理
*/
DEFAULT,
/**
* 插入填充字段
*/
INSERT,
/**
* 更新填充字段
*/
UPDATE,
/**
* 插入和更新填充字段
*/
INSERT_UPDATE
}
5.SQL注入器
SQL 注入器使用步骤
编写自定义注入方法:创建 DeleteAll
、MysqlInsertAllBatch
类,继承自 AbstractMethod
抽象父类,并重写其中的 injectMappedStatement()
方法,为 Mapper 接口的方法注入其对应的 SQL 语句
/**
* 删除全部
*
* @Author Oneby
* @Date 2021/4/27 22:04
*/
public class DeleteAll extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
/* 执行 SQL ,动态 SQL 参考类 SqlMethod */
String sql = "delete from " + tableInfo.getTableName();
/* mapper 接口方法名一致 */
String method = "deleteAll";
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
return this.addDeleteMappedStatement(mapperClass, method, sqlSource);
}
}
/**
* @Author Oneby
* @Date 2021/4/27 22:07
*/
public class MysqlInsertAllBatch extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
final String sql = "<script>insert into %s %s values %s</script>";
final String fieldSql = prepareFieldSql(tableInfo);
final String valueSql = prepareValuesSqlForMysqlBatch(tableInfo);
final String sqlResult = String.format(sql, tableInfo.getTableName(), fieldSql, valueSql);
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlResult, modelClass);
return this.addInsertMappedStatement(mapperClass, modelClass, "mysqlInsertAllBatch", sqlSource, new NoKeyGenerator(), null, null);
}
private String prepareFieldSql(TableInfo tableInfo) {
StringBuilder fieldSql = new StringBuilder();
fieldSql.append(tableInfo.getKeyColumn()).append(",");
tableInfo.getFieldList().forEach(x -> {
fieldSql.append(x.getColumn()).append(",");
});
fieldSql.delete(fieldSql.length() - 1, fieldSql.length());
fieldSql.insert(0, "(");
fieldSql.append(")");
return fieldSql.toString();
}
private String prepareValuesSqlForMysqlBatch(TableInfo tableInfo) {
final StringBuilder valueSql = new StringBuilder();
valueSql.append("<foreach collection=\"list\" item=\"item\" index=\"index\" open=\"(\" separator=\"),(\" close=\")\">");
valueSql.append("#{item.").append(tableInfo.getKeyProperty()).append("},");
tableInfo.getFieldList().forEach(x -> valueSql.append("#{item.").append(x.getProperty()).append("},"));
valueSql.delete(valueSql.length() - 1, valueSql.length());
valueSql.append("</foreach>");
return valueSql.toString();
}
}
6.执行SQL分析打印
使用 SQL 分析打印的步骤
- p6spy 依赖引入
Maven:
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>3.9.1</version>
</dependency>
- application.yml 配置:
- 修改驱动类为 p6spy 提供的驱动类:
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
- 修改 url前的 连接协议:
jdbc:p6spy:mysql
注意:该插件有性能损耗,不建议生产环境使用
# 数据源配置
spring:
datasource:
# driver-class-name: com.mysql.cj.jdbc.Driver
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
url: jdbc:p6spy:mysql://localhost:3307/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
username: root
password: root
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # SQL 日志配置
global-config:
db-config:
logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
logging:
level:
com.baomidou.mybatisplus.samples: debug
spy.properties 配置
#3.2.1以上使用
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
#3.2.1以下使用或者不配置
#modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定义日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# 使用日志系统记录 sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 设置 p6spy driver 代理
deregisterdrivers=true
# 取消JDBC URL前缀
useprefix=true
# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 实际驱动可多个
#driverlist=org.h2.Driver
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准 2 秒
outagedetectioninterval=2
注意!
- driver-class-name 为 p6spy 提供的驱动类
- url 前缀为 jdbc:p6spy 跟着冒号为对应数据库连接地址
- 打印出sql为null,在excludecategories增加commit
- 批量操作不打印sql,去除excludecategories中的batch
- 批量操作打印重复的问题请使用MybatisPlusLogFactory (3.2.1新增)
- 该插件有性能损耗,不建议生产环境使用。
7.数据安全保护
该功能为了保护数据库配置及数据安全,在一定的程度上控制开发人员流动导致敏感信息泄露。
- 3.3.2 开始支持
- 配置安全
YML 配置:
// 加密配置 mpw: 开头紧接加密内容( 非数据库配置专用 YML 中其它配置也是可以使用的 )
spring:
datasource:
url: mpw:qRhvCwF4GOqjessEB3G+a5okP+uXXr96wcucn2Pev6Bf1oEMZ1gVpPPhdDmjQqoM
password: mpw:Hzy5iliJbwDHhjLs1L0j6w==
username: mpw:Xb+EgsyuYRXw7U7sBJjBpA==
密钥加密:
// 生成 16 位随机 AES 密钥
String randomKey = AES.generateRandomKey();
// 随机密钥加密
String result = AES.encrypt(data, randomKey);
如何使用:
// Jar 启动参数( idea 设置 Program arguments , 服务器可以设置为启动环境变量 )
--mpw.key=d1104d7c3b616f0b
8.多数据源
简介
dynamic-datasource-spring-boot-starter 是一个基于springboot的快速集成多数据源的启动器。
其支持 Jdk 1.7+, SpringBoot 1.4.x 1.5.x 2.x.x。
文档 | Documentation
详细文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/2264611
特性
- 支持 数据源分组 ,适用于多种场景 纯粹多库 读写分离 一主多从 混合模式。
- 支持数据库敏感配置信息 加密 ENC()。
- 支持每个数据库独立初始化表结构schema和数据库database。
- 支持无数据源启动,支持懒加载数据源(需要的时候再创建连接)。
- 支持 自定义注解 ,需继承DS(3.2.0+)。
- 提供并简化对Druid,HikariCp,BeeCp,Dbcp2的快速集成。
- 提供对Mybatis-Plus,Quartz,ShardingJdbc,P6sy,Jndi等组件的集成方案。
- 提供 自定义数据源来源 方案(如全从数据库加载)。
- 提供项目启动后 动态增加移除数据源 方案。
- 提供Mybatis环境下的 纯读写分离 方案。
- 提供使用 spel动态参数 解析数据源方案。内置spel,session,header,支持自定义。
- 支持 多层数据源嵌套切换 。(ServiceA >>> ServiceB >>> ServiceC)。
- 提供 **基于seata的分布式事务方案。
- 提供 本地多数据源事务方案。
约定
- 本框架只做 切换数据源 这件核心的事情,并不限制你的具体操作,切换了数据源可以做任何CRUD。
- 配置文件所有以下划线 _ 分割的数据源 首部 即为组的名称,相同组名称的数据源会放在一个组下。
- 切换数据源可以是组名,也可以是具体数据源名称。组名则切换时采用负载均衡算法切换。
- 默认的数据源名称为 master ,你可以通过 spring.datasource.dynamic.primary 修改。
- 方法上的注解优先于类上注解。
- DS支持继承抽象类上的DS,暂不支持继承接口上的DS。
使用方法
- 引入dynamic-datasource-spring-boot-starter。
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>${version}</version>
</dependency>
2.配置数据源。
spring:
datasource:
dynamic:
primary: master #设置默认的数据源或者数据源组,默认值即为master
strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
datasource:
master:
url: jdbc:mysql://xx.xx.xx.xx:3306/dynamic
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置
slave_1:
url: jdbc:mysql://xx.xx.xx.xx:3307/dynamic
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
slave_2:
url: ENC(xxxxx) # 内置加密,使用请查看详细文档
username: ENC(xxxxx)
password: ENC(xxxxx)
driver-class-name: com.mysql.jdbc.Driver
#......省略
#以上会配置一个默认库master,一个组slave下有两个子库slave_1,slave_2
# 多主多从 纯粹多库(记得设置primary) 混合配置
spring: spring: spring:
datasource: datasource: datasource:
dynamic: dynamic: dynamic:
datasource: datasource: datasource:
master_1: mysql: master:
master_2: oracle: slave_1:
slave_1: sqlserver: slave_2:
slave_2: postgresql: oracle_1:
slave_3: h2: oracle_2:
- 使用 @DS 切换数据源。
@DS 可以注解在方法上或类上,同时存在就近原则 方法上注解 优先于 类上注解。
注解 | 结果 |
---|---|
没有@DS | 默认数据源 |
@DS(“dsName”) | dsName可以为组名也可以为具体某个库的名称 |
@Service
@DS("slave")
public class UserServiceImpl implements UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
public List selectAll() {
return jdbcTemplate.queryForList("select * from user");
}
@Override
@DS("slave_1")
public List selectByCondition() {
return jdbcTemplate.queryForList("select * from user where age >10");
}
}