• mybatis-plus 统一管理创建时间、更新时间、创建人与更新人


    前言

    在设计数据库模型的时候,我们通常会在表中创建创建人、创建时间、更新人、更新时间等通用的字段来记录每行数据的创建和变动信息。例如:

    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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    一个系统涉及的模型往往有很多个,每个业务都会有各自对应的逻辑处理,如果在每个表对应的模块都要在各自的业务代码中来维护这些共同的数据,着实浪费!

    那么就将这些可以字段抽取出来统一管理!

    一、数据库模型设计时抽出所有通用字段

    在设计数据库时,定义表的规范,例如:
    每个表都要添加以下的字段信息:

    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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    后期业务迭代添加新表也要遵守以上的表规则

    二、JAVA模型抽象为父类

    /**
     * 基础实体对象
     *
     * @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;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    从上面的基础模型类可以看到以下几个注解:
    @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 }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    以上我们在基础模型中,设置如下:
    创建时间:插入时填充字段(这个其实也可以用mysql的default来管理)
    创建人:插入时填充字段
    更新时间:插入和更新时填充字段(这个其实也可以用mysql的default + on update 来管理)
    更新人:插入和更新时填充字段

    三、JAVA所有业务实体继承该基础模型

    @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; // ... 其他对应的字段属性 }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    四、自定义实现MetaObjectHandler元对象字段填充控制器抽象类,实现公共字段自动写入

    /**
     * 通用参数填充实现类
     *
     * 如果没有显式的对通用参数进行赋值,这里会对通用参数进行填充、赋值
     *
     * @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);
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    结束语

    很简单,以上就管理起来了 !

  • 相关阅读:
    内核网络层的实现
    thinkphp5.0.23漏洞复现以及脚本编写
    【计算机考研】计算机行业考研还有性价比吗?
    飞书机器人相关文档
    在 JavaScript 中循环遍历数组的多种方法
    Spring如何处理线程的并发问题?
    【Mybatis源码】XMLConfigBuilder构建器 - 加载XML与创建Configuration对象的过程
    Git常用命令
    MySQL—MySQL的存储引擎之InnoDB
    初识设计模式 - 单例模式
  • 原文地址:https://blog.csdn.net/zhangsuhua0702/article/details/126894217