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 公共字段自动填充内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!