Mybatis-Plus实现公共字段自动填充的项目实践

 更新时间:2023年07月18日 14:53:08   作者:我爱布朗熊  
本文主要介绍了Mybatis-Plus实现公共字段自动填充的项目实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

一、公共字段自动填充

1.1 问题分析

比如说在新增用户需要指定创建时间、创建人等字段,

修改用户时需要指定修改时间、修改人等字段

这些字段属于公共字段,也就是很多表中都有这些字段

这样我们在以后写起来非常的麻烦, 每次做操作都需要自己手动写。但是对于Mybatis plus来说这些是小意思,它为我们提供了公共字段自动填充功能

1.2 实现思路及代码编写

Mybatis plus 公共字段自动填充,也就是在插入或者更新的时候为指定字段赋予指定的值,使用它的好处就是统一对这些字段进行处理,避免了代码重复。

实现步骤:

1. 在实体类的属性上加入@TableField注解,指定自动填充的策略

    @TableField(value ="create_time",fill = FieldFill.INSERT)    //插入时填充字段
    private LocalDateTime createTime;
    @TableField(value ="update_time",fill = FieldFill.INSERT_UPDATE)//插入和更新时填充字段
    private LocalDateTime updateTime;
    @TableField(value = "create_user",fill = FieldFill.INSERT)//插入时填充字段
    private Long createUser;
    @TableField(value = "update_user",fill = FieldFill.INSERT_UPDATE)//插入和更新时填充字段
    private Long updateUser;

其中FieldFill是一个枚举类,如下所示:

2. 按照框架要求编写元数据对象处理器,在此类中统一为公共字段赋值,此类需要实现MetaObjectHandler接口

package com.reggie_take_out.common;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
/**
 * 元数据对象处理器
 */
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
    /**
     * 执行insert语句的时候执行
     *
     * @param metaObject
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("公共字段自动填充——insertFill");
//      自动填充
        metaObject.setValue("createTime", LocalDateTime.now());
        metaObject.setValue("updateTime", LocalDateTime.now());
//      获取session对象
        metaObject.setValue("createUser", BaseContext.getCurrentId());
        metaObject.setValue("updateUser", BaseContext.getCurrentId());
    }
    /**
     * 执行update语句的时候执行
     *
     * @param metaObject
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("公共字段自动填充——updateFill");
        metaObject.setValue("updateTime", LocalDateTime.now());
        metaObject.setValue("updateUser", BaseContext.getCurrentId());
    }
}

二、 知识补充: ThreadLocal

2.1  使用背景

为什么要使用这个类?

比如我们要给updateUser与createUser字段设置值的时候,我们是通过HttpServletRequest对象获取

 request.getSession().getAttribute("employee");

但是我们在写公共字段自动填充的时候发现不能使用HttpServletRequest。

除此之外,将用户id放入到HttpSession中也是获取不到的,MyMetaObjectHandler类中是不能获取HttpSession对象的所以我们需要其他方式来进行获取。

客户端发送的每次Http请求,对应的服务端都会分配一个新的线程来处理,在处理过程中涉及到下面类中的方法都属于同一个线程

1.LoginCheckFilter 的doFilter方法

2.EmployeeController的update方法

3.MyMetaObjectHandler的updateFill方法  

解决思路:

我们可以在LoginCheckFilter的doFilter方法中获取当前登录用户id,并调用ThreadLocal的set方法来设置当前线程的线程局部变量的值(用户id),然后再MyMetaObjectHandler的updateFill方法中调用ThreadLocal的get方法来获取当前线程所对应的线程局部变量的值(用户id)

2.2 ThreadLocal介绍

  • ThreadLocal并不是一个Thread,而是Thread的局部变量。
  • 当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己副本,而不会影响其他线程所对应的副本。
  • ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。

2.2.1  设置当前线程的线程局部变量的值   public void set(T value)

具体代码查看2.3.1 

2.2.2  返回当前线程所对应的线程  局部变量的值 public T get()

具体代码查看2.3.1 

2.3  实现功能

2.3.1 基于ThreadLocal封装BaseContext工具类

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

2.3.2 在过滤器方法中调用BaseContext工具类设置当前登录用户id

/**
 * 检查用户是否已经完成登录
 * 过滤器与拦截器的区别:Filter对所有访问进行增强(在Tomcat服务器进行配置),Interceptor仅针对SpringMVC的访问进行增强
 */
@Slf4j
@WebFilter(filterName = "loginCheckFilter", urlPatterns = "/*")  //urlPatterns指定拦截哪些路径
public class LoginCheckFilter implements Filter {
    //  此对象的作用:路径匹配器,  匹配路径时支持通配符
    public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//      servletRequest向下强制类型转换
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        //1. 获取本次请求的URI( URI:请求的资源路径)
        String requestURI = request.getRequestURI();
        log.info("拦截到请求:{}", request.getRequestURI());
        // 定义不用处理的请求路径
        String[] urls = new String[]{
                "/employee/login",
                "/employee/logout",
                "/backend/**",
                "/front/**"
        };
        //2. 判断本次请求是否需要处理(因为有些请求并不需要用户登录)
        boolean check = check(requestURI, urls);
        //3.如果不需要处理,则直接放行
        if (check) {
            log.info("本次请求{}不需要处理", request.getRequestURI());
            filterChain.doFilter(request, response);
            return;
        }
        //4.判断登录状态,如果已登录,则直接放行.从session中获取用户,如果获取到说明已经登录
        if (request.getSession().getAttribute("employee") != null) {
            log.info("用户已登录,用户id为{}", request.getSession().getAttribute("employee"));
            Long empId = (Long) request.getSession().getAttribute("employee");
            BaseContext.setCurrentId(empId);
            filterChain.doFilter(request, response);
            return;
        }
        //5.如果未登录则返回未登录结果
        log.info("资源路径路径:{},用户未登录{}", request.getRequestURI(), request.getSession().getAttribute("employee"));
//           通过输出流的方式向客户端响应数据   (为什么要返回这个NOTLOGIN?  因为前端需要这个来进行判定是否登录)
        response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
//        filterChain.doFilter(request, response);  加上这个就无法实现
    }
    /**
     * 检查本次请求是否需要放行
     *
     * @param requestURI 请求的资源路径
     * @param urls       放过的路径
     * @return true 放行
     */
    public boolean check(String requestURI, String[] urls) {
        for (String url : urls) {
            boolean match = PATH_MATCHER.match(url, requestURI);
            if (match) {
//           放行
                return true;
            }
        }
        return false;
    }
}

2.3.3 在MyMetaObjectHandler的方法中调用BaseContext工具类获取登录用户id

package com.reggie_take_out.common;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
/**
 * 元数据对象处理器
 */
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
    /**
     * 执行insert语句的时候执行
     *
     * @param metaObject
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("公共字段自动填充——insertFill");
//      自动填充
        metaObject.setValue("createTime", LocalDateTime.now());
        metaObject.setValue("updateTime", LocalDateTime.now());
//      获取session对象
        metaObject.setValue("createUser", BaseContext.getCurrentId());
        metaObject.setValue("updateUser", BaseContext.getCurrentId());
    }
    /**
     * 执行update语句的时候执行
     *
     * @param metaObject
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("公共字段自动填充——updateFill");
        metaObject.setValue("updateTime", LocalDateTime.now());
        metaObject.setValue("updateUser", BaseContext.getCurrentId());
    }
}

到此这篇关于Mybatis-Plus实现公共字段自动填充的项目实践的文章就介绍到这了,更多相关Mybatis-Plus 公共字段自动填充内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • springboot中swagger、异步/定时/邮件任务的问题

    springboot中swagger、异步/定时/邮件任务的问题

    这篇文章主要介绍了springboot中swagger、异步/定时/邮件任务的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-07-07
  • SpringBoot集成RocketMQ发送事务消息的原理解析

    SpringBoot集成RocketMQ发送事务消息的原理解析

    RocketMQ 的事务消息提供类似 X/Open XA 的分布事务功能,通过事务消息能达到分布式事务的最终一致,这篇文章主要介绍了SpringBoot集成RocketMQ发送事务消息,需要的朋友可以参考下
    2022-06-06
  • 如何理解Java线程池及其使用方法

    如何理解Java线程池及其使用方法

    线程池是首先创建一些线程,它们的集合称为线程池。使用线程池可以提高性能,它在系统启动时创建大量空闲的线程,程序将一个任务传给线程池,它就会启动一条线程来执行这个任务,执行结束以后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个任务
    2021-06-06
  • 一文带你了解SpringBoot的停机方式

    一文带你了解SpringBoot的停机方式

    停机简单的说,就是向应用进程发出停止指令之后,能保证正在执行的业务操作不受影响,直到操作运行完毕之后再停止服务。本文就来和大家聊聊Springboot的停机方式与停机处理
    2023-02-02
  • Java中的Object类详细解读

    Java中的Object类详细解读

    这篇文章主要介绍了Java中的Object类详细解读,java.lang.Object是类层次结构的根类,即所有其它类的父类,每个类都使用 Object 作为超类,需要的朋友可以参考下
    2023-11-11
  • Dubbo Consumer引用服务示例代码详解

    Dubbo Consumer引用服务示例代码详解

    dubbo中引用远程服务有两种方式:服务直连(不经过注册中心)、基于注册中心引用服务,在实际线上环境中我们基本上使用的都是基于注册中心引用服务的方式,下面我们就围绕该方式讲解分析
    2023-03-03
  • Java 调用天气Webservice详解及实例代码

    Java 调用天气Webservice详解及实例代码

    这篇文章主要介绍了Java 调用天气Webservice详解及实例代码的相关资料,这里附实例代码,使用java 调用webservice 的小应用,需要的朋友可以参考下
    2016-11-11
  • java并发分段锁实践代码

    java并发分段锁实践代码

    这篇文章主要介绍了java并发分段锁实践代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-07-07
  • Springboot整合fastdfs实现分布式文件存储

    Springboot整合fastdfs实现分布式文件存储

    本文主要介绍了Springboot整合fastdfs实现分布式文件存储,详细阐述了Springboot应用程序如何与FastDFS进行集成及演示了如何使用Springboot和FastDFS实现分布式文件存储,感兴趣的可以了解一下
    2023-08-08
  • Java中关于isEmpty方法、null以及““的区别

    Java中关于isEmpty方法、null以及““的区别

    这篇文章主要介绍了Java中关于isEmpty方法、null以及““的区别,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-08-08

最新评论