SpringBoot ThreadLocal实现公共字段自动填充案例讲解

目录
  • 一.字段自动填充引入
  • 二.元数据对象处理器
  • 三.Threadlocal的使用

一.字段自动填充引入

先看一个现象,在之前写好的表中,我们发现有很多字段重复出现

比如update_time、create_time、create_user…

这就导致需要在Controller层中每一次对表中数据进行修改后调用一次.setCreateTime(LocalDateTime.now());或者setUpdateTime(LocalDateTime.now());等等“硬编码问题又出现了”显得格外麻烦

这些共性字段如何统一拿出来处理呢?MyBatisPlus给了我们解决方案,为了实现这一功能:

首先

我们需要在公共字段对应的实体属性上加上@TableField注解与指定填充策略,就像这样:

    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    @TableField(fill = FieldFill.INSERT)
    private Long createUser;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUser;

不难看出,fill的类型是一个枚举类

若是不需要处理则选择DEAFULT,若是增添后需要修改字段的值选择INSERT,若是涉及修改数据时需要修改字段的值则选择UPDATE,若是插入和修改都需要设置字段的值则选择INSERT_UPDATE就像updateTime和updateUser,只要涉及对数据的操作就要修改字段的值

类似于全局异常处理器,为了实现字段填充也需要定义一个元数据对象处理器

二.元数据对象处理器

在定义的类中实现MetaObjecthandler接口,并重写接口中策略对应的方法,就像这样:

@Component
@Slf4j
public class MyMetaObjecthandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("公共字段自动填充...");
        log.info(metaObject.toString());
    }
    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("公共字段自动填充...");
        log.info(metaObject.toString());
    }
}

这里有点像继承HttpServlet那个套路,重写doGet()与doPost()方法,要实现什么功能就写在对应的方法里…

重写完方法,不妨debug一下:

发现我实现接口后在方法里拿到了实体的数据,并封装到了形参里的metaObject对象中,接下来我就可以利用此对象来做公共字段的集中处理了!

所谓的自动填充也正是因为该类拥有@Component注解,在每一次的项目启动后就会被扫描到,加载到,而类中的方法又实现了功能,所以才可以做到自动填充字段!

回到正题

我们要做的是把公共的字段做到统一填充,具体实现则是在重写的方法里调用setValue()方法并传进去字段与参数,就像这样:

    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("公共字段自动填充...");
        log.info(metaObject.toString());
        metaObject.setValue("createTime", LocalDateTime.now());
        metaObject.setValue("updateTime",LocalDateTime.now());
    }
    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("公共字段自动填充...");
        log.info(metaObject.toString());
        metaObject.setValue("updateTime",LocalDateTime.now());
    }

相比之前controller层中的employee.setCreateTime(LocalDateTime.now());/employee.setUpdateTime(LocalDateTime.now());来看还是比较好理解,无非就是“茴“字的另一种写法

(但却实现了一劳永逸)…

可能有人会问,懒羊羊你的updateUser与createUser字段怎么不处理呢?

确实,还记得之前在controller层中我们是怎么处理的吗?

为了确定User是谁,我们从Session里拿到了操作的对象id,并调用set方法修改了对象的字段值

按照上面的经验,为了能够动态的拿到id,我们应该这样设置:

可是,在此方法中metaObject对象好像不能利用Session里的empID,那要如何获得对象的标识呢?

不就是一个标识么,我用线程id可以吗?

三.Threadlocal的使用

在这之前,需要明白一点,每当客户端发送一次HTTP请求,对应在服务器端会分配一个新的线程来处理。项目设计到现在为止,以点击一次保存按钮作为一次请求,它会触发过滤器、调用Controller层、MetaObjectHandler层的方法

我们通过Thread的内部方法:long id = Thread.currentThread().getId();来获得当前线程的id,以日志的形式log.info("当前线程id:{}",id);输出到控制台:

可见,三者的线程id相同,说明他们在同一个线程中,这就保证了一致性,也正是因为这个特性,所以可用来当作表中的字段id使用来作为标识

在此基础上,大致方向已经敲定是利用线程的特性,具体要如何实现呢?那就不得不需要了解一下ThreadLocal这个概念了:

1.ThreadLocal并不是一个Thread,而是Thread的局部变量。

2.当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

3.ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问

常用方法

public void set(T value) 设置当前线程局部变量的值
public T get() 返回当前线程所对应的线程局部变量的值

了解到ThreadLocal特性,我们就可以结合“同一线程”这一特性来获得那个对象唯一的SessionId。

在前面的登录功能中我们设置了一个filter,在filter中我们已经拿到过了一次SessionId,写到这里解决方案不就出来了嘛——把filter中的SessionId当作参数传给ThreadLocal的set方法,然后在MetaObjectHandler实现类(元数据对象处理器)中调用ThreadLocal的get()方法得到SessionId

就像这样:

1.为了规范,我们封装一个类,功能是调用ThreadLocal的方法

/**
 * 基于ThreadLocal封装工具类,用于保护和获取当前登录id
 */
public class BaseContext {
    private static ThreadLocal<Long> threadLocal=new ThreadLocal();
    public static void setCurrentId(Long currentId){
        threadLocal.set(currentId);
    }
    public static Long getCurrentId(){
        return threadLocal.get();
    }
}

2.在filter中做到SessionId的迁移

3.在MetaObjectHandler实现类中利用SessionId完成公共字段填充设置

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("公共字段自动填充...");
        log.info(metaObject.toString());
        long id = Thread.currentThread().getId();
        log.info("当前线程id:{}",id);
        metaObject.setValue("updateTime",LocalDateTime.now());
        metaObject.setValue("createUser",BaseContext.getCurrentId());
        metaObject.setValue("updateUser",BaseContext.getCurrentId());
    }

这样,就可以一劳永逸咯~

又是一个小技巧啊!

到此这篇关于SpringBoot ThreadLocal实现公共字段自动填充案例讲解的文章就介绍到这了,更多相关SpringBoot自动填充内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • springboot 使用ThreadLocal的实例代码

    目录 springboot 使用ThreadLocal ThreadLocal在springboot使用中的坑 springboot 使用ThreadLocal 本文参考慕课教程给出一个在spring boot中使用ThreadLocal实现线程封闭的实例. 首先创建一个包含ThreadLocal成员变量的实例: public class RequestHolder { private final static ThreadLocal<Long> requestHolder = new Thr

  • Springboot公共字段填充及ThreadLocal模块改进方案

    目录 1. 公共字段自动填充 1.1 问题分析 1.2 基本功能实现 1.2.1 思路分析 1.3 代码实现 1). 实体类的属性上加入@TableField注解,指定自动填充的策略. 2). 按照框架要求编写元数据对象处理器,在此类中统一为公共字段赋值,此类需要实现MetaObjectHandler接口. 2 使用ThreadLocal对公共字段填充功能进行完善 2.1 思路分析 2.1.1 提出设想 2.1.2 分析问题 2.2 ThreadLocal 2.2.1 ThreadLocal基本

  • SpringBoot通过ThreadLocal实现登录拦截详解流程

    目录 1 前言 2 具体类 2.1HandlerInterceptor 2.2WebMvcConfigurer 3 代码实践 1 前言 注册登录可以说是平时开发中最常见的东西了,但是一般进入到公司之后,像这样的功能早就开发完了,除非是新的项目.这两天就碰巧遇到了这样一个需求,完成pc端的注册登录功能. 实现这样的需求有很多种方式:像 1)HandlerInterceptor+WebMvcConfigurer+ThreadLocal 2)Filter过滤器 3)安全框架Shiro(轻量级框架) 4

  • SpringBoot Mybatis Plus公共字段自动填充功能

    一.应用场景 平时在建对象表的时候都会有最后修改时间,最后修改人这两个字段,对于这些大部分表都有的字段,每次在新增和修改的时候都要考虑到这几个字段有没有传进去,很麻烦.mybatisPlus有一个很好的解决方案.也就是公共字段自动填充的功能.一般满足下面条件的字段就可以使用此功能: 这个字段是大部分表都会有的. 这个字段的值是固定的,或则字段值是可以在后台动态获取的. 常用的就是last_update_time,last_update_name这两个字段. 二.配置MybatisPlus 导包:

  • MyBatis-Plus实现公共字段自动填充功能详解

    目录 1.问题分析 2.实现步骤 3. 实现字段全局填充 1.问题分析 我们在开发中经常遇到多个实体类有共同的属性字段,例如在用户注册时需要设置创建时间.创建人.修改时间.修改人等字段,在用户编辑信息时需要设置修改时间和修改人等字段.这些字段属于公共字段,也就是很多表中都有这些字段,能不能对于这些公共字段在某个地方统一处理,来简化开发呢? 答案就是我们可是使用Mybatis Plus提供的公共字段自动填充功能. 2.实现步骤 Mybatis Plus公共字段自动填充,也就是在插入或者更新的时候为

  • MyBatis-Plus实现字段自动填充功能的示例

    目录 一.前言 二.实现 1. 实体类 2. 公用字段 - 使用注解填充字段 3. 自定义MyMetaObjectHandler字段自动填充处理类继承MetaObjectHandler 一.前言 在项目中,我们有一些公共的字段需要做修改 如: gmt_create:创建时间 creator_id:创建人 gmt_modified:修改时间 modifier_id:修改人 这时候我们可以采用 MyBatis-Plus 中的字段自动填充功能去实现 思路:抽取公用字段封装到BaseEntity类中,再

  • Mybatis-Plus实现公共字段自动赋值的方法

    目录 1 mybatis-plus实现自动填充指定字段 1.1 需要修改的字段在模型类上添加 1.2 新建一个MetaObjectHandler的实现类 2 通用字段自动填充的最佳实践总结 2.1 调整数据库表结构 2.2 通用维护信息父类-自动赋值的字段 2.3 实体类的实现 2.4 自动赋值的规则 2.5 实现效果 1 mybatis-plus实现自动填充指定字段 首先了解MetaObjectHandler MetaObjectHandler接口是mybatisPlus为我们提供的的一个扩展

  • mybatis-plus主键id生成、字段自动填充的实现代码

    一.主键id的生成 数据库表里通常都会有一个主键id,来作为这条数据的唯一标识. 常见的方式 1.数据库自动增长 这种很常见了,可以做到全库唯一.因为id是天然排序的,对于涉及到排序的操作会很方便. 2.UUID 上面的自动增长,虽然简单,但是对于分表这样的操作来说就比较麻烦.因为你在第二张插入数据的时候,需要拿到上一张表最后一个数据的id. UUID则不同,每次都一个随机唯一的值,不过因为是随机,所以也就没有排序了. 3.redis redis也可以用来生成id,利用redis的原子操作.好处

  • Mybatis plus通用字段自动填充的示例

    在进行持久层数据维护(新增或修改)的时候,我们通常需要记录一些非业务字段,比如:create_time.update_time.update_by.create_by等用来维护数据记录的创建时间.修改时间.修改人.创建人等信息.通常情况下我们需要对这些字段进行手动赋值.赋值的过程也比较冗余,都是重复操作. 通常是为create_time赋值为系统的当前时间.update_time赋值为系统修改操作执行时的当前时间. create_by(创建人).update_by(修改人)赋值为当前的登录用户的

  • MyBatis-Plus逻辑删除和字段自动填充的实现

    目录 一.ID生成策略 1.使用@TableId注解 2.全局ID生成策略 二.逻辑删除 1.全局配置 2.使用@TableLogic注解 三.字段自动填充 1.指定字段自动填充 2.自定义MetaObjectHandler 3.测试 四.执行SQL分析打印 1.添加 spy.properties配置文件 2.修改数据源配置 3.测试查询 一.ID生成策略 1.使用@TableId注解 @TableId注解:主键注解 使用位置:实体类主键字段. @Data @ToString @TableNam

  • 在MyBatisPlus中使用@TableField完成字段自动填充的操作

    场景 官方文档: 字段注解 @TableField com.baomidou.mybatisplus.annotations.TableField 值 描述 value 字段值(驼峰命名方式,该值可无) update 预处理 set 字段自定义注入 condition 预处理 WHERE 实体条件自定义运算规则 el 详看注释说明 exist 是否为数据库表字段( 默认 true 存在,false 不存在 ) strategy 字段验证 ( 默认 非 null 判断,查看 com.baomido

  • Mybatis-Plus3.2.0 MetaObjectHandler 无法进行公共字段全局填充

    问题描述 最近在做的这个项目架构组对于配置文件决定,采取的是 .xml 配置文件 一个数据中台项目,因为部署环境比较复杂,可能需要适配阿里.腾讯.开源等环境和机房,所以配置文件和启动类为三类 之前写的 MetaObjectHandler 都是采用的 SpringBoot 配置方式开发,突然换成 .xml 配置文件,还是有不少坑的,其中就有配置的 MetaObjectHandler 死活不起作用 ⬆️

  • SQL语句中公共字段的自动填充方法

    1. 前言 我们在设计数据库的时候一定会带上新增.更新的时间.操作者等审计信息. 之所以带这些信息是因为假如有一天公司的数据库被人为删了,尽管可能有数据库备份可以恢复数据.但是我们仍然需要追踪到这个事是谁干的,在什么时间干的,具体干了哪些事等等,方便定责和修补.但是我们变更每条数据都要去显式变更这些信息就十分繁琐,我们希望无感知的来处理这些信息. 2. 通用方式 那么有什么好的解决思路呢?在Spring Data框架中提供@CreatedBy和@LastModifiedBy来捕捉谁创建或修改的实

随机推荐