在设计数据库模型的时候,我们通常会在表中创建创建人、创建时间、更新人、更新时间等通用的字段来记录每行数据的创建和变动信息。例如:
create table table_demo
(
id bigint auto_increment comment '菜单ID'
primary key,
//.... 其它业务表字段
creator varchar(64) default '' null comment '创建者',
create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间',
updater varchar(64) default '' null comment '更新者',
update_time datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
deleted bit default b'0' not null comment '是否删除'
)
comment 'XXX表' collate = utf8mb4_unicode_ci;
一个系统涉及的模型往往有很多个,每个业务都会有各自对应的逻辑处理,如果在每个表对应的模块都要在各自的业务代码中来维护这些共同的数据,着实浪费!
那么就将这些可以字段抽取出来统一管理!
在设计数据库时,定义表的规范,例如:
每个表都要添加以下的字段信息:
create table table_demo
(
id bigint auto_increment comment '菜单ID'
primary key,
//.... 其它业务表字段
creator varchar(64) default '' null comment '创建者',
create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间',
updater varchar(64) default '' null comment '更新者',
update_time datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
deleted bit default b'0' not null comment '是否删除'
)
comment 'XXX表' collate = utf8mb4_unicode_ci;
后期业务迭代添加新表也要遵守以上的表规则
/**
* 基础实体对象
*
* @author 许仙许仙
*/
@Data
public abstract class BaseEntity implements Serializable {
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private Date createTime;
/**
* 最后更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
/**
* 创建者,目前使用 SysUser 的 id 编号
*
* 使用 String 类型的原因是,未来可能会存在非数值的情况,留好拓展性。
*/
@TableField(fill = FieldFill.INSERT)
private String creator;
/**
* 更新者,目前使用 SysUser 的 id 编号
*
* 使用 String 类型的原因是,未来可能会存在非数值的情况,留好拓展性。
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private String updater;
/**
* 是否删除
*/
@TableLogic
private Boolean deleted;
}
从上面的基础模型类可以看到以下几个注解:
@TableLogic
框架提供的逻辑删除注解。
配合以下配置一起使用:
mybatis-plus.configuration.global-config.db-config.logic-delete-value=1 ##字段值为1时 表示逻辑删除
mybatis-plus.configuration.global-config.db-config.logic-delete-value=0 ##字段值为0时 表示未删除
当字段添加上@TableLogic后 框架在生成sql模板时 自动在后面添加上逻辑删除字段=0(配置文件中的配置) 的查询条件
@TableField
该注解用于标识非主键的字段。
存在多个属性,各个属性的用法如下:
value :指定映射的数据库字段名
el:映射为原生 #{ … } 逻辑,相当于写在 xml 里的 #{ … } 部分。
exist:是否为数据库表字段,默认为 true。
condition:字段 where 实体查询比较条件,有值设置则按设置的值为准,没有则为默认全局的 %s=#{%s}。
update:字段 update set 部分注入,例如:update=“%s+1”:表示更新时会 set version=version+1(该属性优先级高于 el 属性)。
fill:字段自动填充策略,默认为 FieldFill.DEFAULT。
… 其他的自己去官网看…
本文主要用到 fill的自动填充策略
填充策略如下,mybatis-plus的源码注释写的很清楚
package com.baomidou.mybatisplus.annotation;
/**
* 字段填充策略枚举类
*
*
* 判断注入的 insert 和 update 的 sql 脚本是否在对应情况下忽略掉字段的 if 标签生成
* ......
* 判断优先级比 {@link FieldStrategy} 高
*
*
* @author hubin
* @since 2017-06-27
*/
public enum FieldFill {
/**
* 默认不处理
*/
DEFAULT,
/**
* 插入时填充字段
*/
INSERT,
/**
* 更新时填充字段
*/
UPDATE,
/**
* 插入和更新时填充字段
*/
INSERT_UPDATE
}
以上我们在基础模型中,设置如下:
创建时间:插入时填充字段(这个其实也可以用mysql的default来管理)
创建人:插入时填充字段
更新时间:插入和更新时填充字段(这个其实也可以用mysql的default + on update 来管理)
更新人:插入和更新时填充字段
@TableName(value = "对应的表名称", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MyTableEntity extends BaseEntity {
/**
* 用户ID
*/
@TableId
private Long id;
/**
* 用户账号
*/
private String username;
/**
* 加密后的密码
*
* 因为目前使用 {@link BCryptPasswordEncoder} 加密器,所以无需自己处理 salt 盐
*/
private String password;
// ... 其他对应的字段属性
}
/**
* 通用参数填充实现类
*
* 如果没有显式的对通用参数进行赋值,这里会对通用参数进行填充、赋值
*
* @author 许仙许仙
*/
public class DefaultDBFieldHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
if (Objects.nonNull(metaObject) && metaObject.getOriginalObject() instanceof BaseDO) {
BaseDO baseDO = (BaseDO) metaObject.getOriginalObject();
Date current = new Date();
// 创建时间为空,则以当前时间为插入时间
if (Objects.isNull(baseDO.getCreateTime())) {
baseDO.setCreateTime(current);
}
// 更新时间为空,则以当前时间为更新时间
if (Objects.isNull(baseDO.getUpdateTime())) {
baseDO.setUpdateTime(current);
}
String userId = ..... 这里自定义实现逻辑,获取当前登录用户
// 当前登录用户不为空,创建人为空,则当前登录用户为创建人
if (Objects.nonNull(userId) && Objects.isNull(baseDO.getCreator())) {
baseDO.setCreator(userId.toString());
}
// 当前登录用户不为空,更新人为空,则当前登录用户为更新人
if (Objects.nonNull(userId) && Objects.isNull(baseDO.getUpdater())) {
baseDO.setUpdater(userId.toString());
}
}
}
@Override
public void updateFill(MetaObject metaObject) {
// 更新时间为空,则以当前时间为更新时间
Object modifyTime = getFieldValByName("updateTime", metaObject);
if (Objects.isNull(modifyTime)) {
setFieldValByName("updateTime", new Date(), metaObject);
}
// 当前登录用户不为空,更新人为空,则当前登录用户为更新人
Object modifier = getFieldValByName("updater", metaObject);
String userId = ..... 这里自定义实现逻辑,获取当前登录用户
if (Objects.nonNull(userId) && Objects.isNull(modifier)) {
setFieldValByName("updater", userId.toString(), metaObject);
}
}
}
很简单,以上就管理起来了 !