【SpringBoot+MyBatisPlus】利用线程特性与ThreadLocal来解决公共字段自动填充问题

简介: 利用线程特性与ThreadLocal来解决公共字段自动填充问题

前言

每一次在Controller层中封装改动数据的方法时都要重新设置一些共性字段,显得十分冗余。为了解决此问题也是在项目中第一次利用到线程,总的来说还是让我眼前一亮,也开阔了视野,对以后的开发具有深远的意义!

一.字段自动填充引入

先看一个现象,在之前写好的表中,我们发现有很多字段重复出现
在这里插入图片描述
比如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());
    }

这样,就可以一劳永逸咯~
又是一个小技巧啊!

相关文章
|
11天前
|
前端开发 JavaScript Java
技术分享:使用Spring Boot3.3与MyBatis-Plus联合实现多层次树结构的异步加载策略
在现代Web开发中,处理多层次树形结构数据是一项常见且重要的任务。这些结构广泛应用于分类管理、组织结构、权限管理等场景。为了提升用户体验和系统性能,采用异步加载策略来动态加载树形结构的各个层级变得尤为重要。本文将详细介绍如何使用Spring Boot3.3与MyBatis-Plus联合实现这一功能。
44 2
|
21天前
|
Java 数据库连接 测试技术
SpringBoot 3.3.2 + ShardingSphere 5.5 + Mybatis-plus:轻松搞定数据加解密,支持字段级!
【8月更文挑战第30天】在数据驱动的时代,数据的安全性显得尤为重要。特别是在涉及用户隐私或敏感信息的应用中,如何确保数据在存储和传输过程中的安全性成为了开发者必须面对的问题。今天,我们将围绕SpringBoot 3.3.2、ShardingSphere 5.5以及Mybatis-plus的组合,探讨如何轻松实现数据的字段级加解密,为数据安全保驾护航。
72 1
|
1月前
|
Java 关系型数据库 MySQL
1、Mybatis-Plus 创建SpringBoot项目
这篇文章是关于如何创建一个SpringBoot项目,包括在`pom.xml`文件中引入依赖、在`application.yml`文件中配置数据库连接,以及加入日志功能的详细步骤和示例代码。
|
14天前
|
Java 数据库连接 开发者
MyBatis-Plus整合SpringBoot及使用
MyBatis-Plus为MyBatis提供了强大的增强,使得在Spring Boot项目中的数据访问层开发变得更加快捷和简便。通过MyBatis-Plus提供的自动CRUD、灵活的查询构造器和简洁的配置,开发者
29 0
|
1月前
|
数据库
elementUi使用dialog的进行信息的添加、删除表格数据时进行信息提示。删除或者添加成功的信息提示(SpringBoot+Vue+MybatisPlus)
这篇文章介绍了如何在基于SpringBoot+Vue+MybatisPlus的项目中使用elementUI的dialog组件进行用户信息的添加和删除操作,包括弹窗表单的设置、信息提交、数据库操作以及删除前的信息提示和确认。
elementUi使用dialog的进行信息的添加、删除表格数据时进行信息提示。删除或者添加成功的信息提示(SpringBoot+Vue+MybatisPlus)
|
1月前
|
JavaScript Java 数据库
Vue+SpringBoot+ElementUi+mybatis-plus 实现用户信息的修改及模拟充值
这篇文章展示了如何使用Vue结合SpringBoot、ElementUI和mybatis-plus实现用户信息的修改以及模拟充值的功能。文章首先介绍了模拟充值的过程,包括充值前后的账户余额和数据库信息的截图。然后,文章展示了用户信息修改前后的界面和数据库信息。核心代码部分演示了如何使用mybatis-plus轻松实现用户信息的修改操作,同时指出了异常处理和代码组织的最佳实践。
|
1月前
|
Java UED
基于SpringBoot自定义线程池实现多线程执行方法,以及多线程之间的协调和同步
这篇文章介绍了在SpringBoot项目中如何自定义线程池来实现多线程执行方法,并探讨了多线程之间的协调和同步问题,提供了相关的示例代码。
174 0
|
1月前
|
druid Java 数据库连接
SpringBoot项目整合MybatisPlus持久层框架+Druid数据库连接池,以及实现增删改查功能
SpringBoot项目整合MybatisPlus和Druid数据库连接池,实现基本的增删改查功能。
157 0
|
6天前
|
缓存 前端开发 Java
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
Soring Boot的起步依赖、启动流程、自动装配、常用的注解、Spring MVC的执行流程、对MVC的理解、RestFull风格、为什么service层要写接口、MyBatis的缓存机制、$和#有什么区别、resultType和resultMap区别、cookie和session的区别是什么?session的工作原理
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
|
30天前
|
Web App开发 前端开发 关系型数据库
基于SpringBoot+Vue+Redis+Mybatis的商城购物系统 【系统实现+系统源码+答辩PPT】
这篇文章介绍了一个基于SpringBoot+Vue+Redis+Mybatis技术栈开发的商城购物系统,包括系统功能、页面展示、前后端项目结构和核心代码,以及如何获取系统源码和答辩PPT的方法。