【瑞吉外卖】day05:增、删、改、查分类以及公共字段自动填充(一)

简介: 【瑞吉外卖】day05:增、删、改、查分类以及公共字段自动填充

1. 公共字段自动填充


1.1 问题分析


前面我们已经完成了后台系统的员工管理功能的开发,在新增员工时需要设置创建时间、创建人、修改时间、修改人等字段,在编辑员工时需要设置修改时间、修改人等字段。这些字段属于公共字段,也就是也就是在我们的系统中很多表中都会有这些字段,如下:

image.png

而针对于这些字段,我们的赋值方式为:

A. 在新增数据时, 将createTime、updateTime 设置为当前时间, createUser、updateUser设置为当前登录用户ID。

B. 在更新数据时, 将updateTime 设置为当前时间, updateUser设置为当前登录用户ID。

目前,在我们的项目中处理这些字段都是在每一个业务方法中进行赋值操作,如下:

image.png

image.png

如果都按照上述的操作方式来处理这些公共字段, 需要在每一个业务方法中进行操作, 编码相对冗余、繁琐,那能不能对于这些公共字段在某个地方统一处理,来简化开发呢?

答案是可以的,我们使用Mybatis Plus提供的公共字段自动填充功能。

1.2 基本功能实现


1.2.1 思路分析


Mybatis Plus公共字段自动填充,也就是在插入或者更新的时候为指定字段赋予指定的值,使用它的好处就是可以统一对这些字段进行处理,避免了重复代码。在上述的问题分析中,我们提到有四个公共字段,需要在新增/更新中进行赋值操作, 具体情况如下:

字段名 赋值时机 说明
createTime 插入(INSERT) 当前时间
updateTime 插入(INSERT) , 更新(UPDATE) 当前时间
createUser 插入(INSERT) 当前登录用户ID
updateUser 插入(INSERT) , 更新(UPDATE) 当前登录用户ID

实现步骤:

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

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

1.2.2 代码实现


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

在员工Employee实体类的公共字段属性上, 加上注解, 指定填充策略。(ps.在资料中提供的实体类,已经添加了该注解,并指定了填充策略)

image.png

FieldFill.INSERT: 插入时填充该属性值

FieldFill.INSERT_UPDATE: 插入/更新时填充该属性值

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

所属包: com.itheima.reggie.common

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#770088">import</span> <span style="color:#000000">com</span>.<span style="color:#000000">baomidou</span>.<span style="color:#000000">mybatisplus</span>.<span style="color:#000000">core</span>.<span style="color:#000000">handlers</span>.<span style="color:#000000">MetaObjectHandler</span>;
<span style="color:#770088">import</span> <span style="color:#000000">lombok</span>.<span style="color:#000000">extern</span>.<span style="color:#000000">slf4j</span>.<span style="color:#000000">Slf4j</span>;
<span style="color:#770088">import</span> <span style="color:#000000">org</span>.<span style="color:#000000">apache</span>.<span style="color:#000000">ibatis</span>.<span style="color:#000000">reflection</span>.<span style="color:#000000">MetaObject</span>;
<span style="color:#770088">import</span> <span style="color:#000000">org</span>.<span style="color:#000000">springframework</span>.<span style="color:#000000">stereotype</span>.<span style="color:#000000">Component</span>;
<span style="color:#770088">import</span> <span style="color:#000000">java</span>.<span style="color:#000000">time</span>.<span style="color:#000000">LocalDateTime</span>;
<span style="color:#aa5500">/**</span>
 <span style="color:#aa5500">* 自定义元数据对象处理器</span>
 <span style="color:#aa5500">*/</span>
<span style="color:#555555">@Component</span>
<span style="color:#555555">@Slf4j</span>
<span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">MyMetaObjecthandler</span> <span style="color:#770088">implements</span> <span style="color:#000000">MetaObjectHandler</span> {
    <span style="color:#aa5500">/**</span>
     <span style="color:#aa5500">* 插入操作,自动填充</span>
     <span style="color:#aa5500">* @param metaObject</span>
     <span style="color:#aa5500">*/</span>
    <span style="color:#555555">@Override</span>
    <span style="color:#770088">public</span> <span style="color:#008855">void</span> <span style="color:#000000">insertFill</span>(<span style="color:#000000">MetaObject</span> <span style="color:#000000">metaObject</span>) {
        <span style="color:#000000">log</span>.<span style="color:#000000">info</span>(<span style="color:#aa1111">"公共字段自动填充[insert]..."</span>);
        <span style="color:#000000">log</span>.<span style="color:#000000">info</span>(<span style="color:#000000">metaObject</span>.<span style="color:#000000">toString</span>());
        <span style="color:#000000">metaObject</span>.<span style="color:#000000">setValue</span>(<span style="color:#aa1111">"createTime"</span>, <span style="color:#000000">LocalDateTime</span>.<span style="color:#000000">now</span>());
        <span style="color:#000000">metaObject</span>.<span style="color:#000000">setValue</span>(<span style="color:#aa1111">"updateTime"</span>,<span style="color:#000000">LocalDateTime</span>.<span style="color:#000000">now</span>());
        <span style="color:#000000">metaObject</span>.<span style="color:#000000">setValue</span>(<span style="color:#aa1111">"createUser"</span>,<span style="color:#770088">new</span> <span style="color:#008855">Long</span>(<span style="color:#116644">1</span>));
        <span style="color:#000000">metaObject</span>.<span style="color:#000000">setValue</span>(<span style="color:#aa1111">"updateUser"</span>,<span style="color:#770088">new</span> <span style="color:#008855">Long</span>(<span style="color:#116644">1</span>));
    }
    <span style="color:#aa5500">/**</span>
     <span style="color:#aa5500">* 更新操作,自动填充</span>
     <span style="color:#aa5500">* @param metaObject</span>
     <span style="color:#aa5500">*/</span>
    <span style="color:#555555">@Override</span>
    <span style="color:#770088">public</span> <span style="color:#008855">void</span> <span style="color:#000000">updateFill</span>(<span style="color:#000000">MetaObject</span> <span style="color:#000000">metaObject</span>) {
        <span style="color:#000000">log</span>.<span style="color:#000000">info</span>(<span style="color:#aa1111">"公共字段自动填充[update]..."</span>);
        <span style="color:#000000">log</span>.<span style="color:#000000">info</span>(<span style="color:#000000">metaObject</span>.<span style="color:#000000">toString</span>());
        <span style="color:#000000">metaObject</span>.<span style="color:#000000">setValue</span>(<span style="color:#aa1111">"updateTime"</span>,<span style="color:#000000">LocalDateTime</span>.<span style="color:#000000">now</span>());
        <span style="color:#000000">metaObject</span>.<span style="color:#000000">setValue</span>(<span style="color:#aa1111">"updateUser"</span>,<span style="color:#770088">new</span> <span style="color:#008855">Long</span>(<span style="color:#116644">1</span>));
    }
}</span></span>

1.2.3 功能测试


编写完了元数据对象处理器之后,我们就可以将之前在新增和修改方法中手动赋值的代码删除或注释掉。

image.png

image.png

然后,我们启动项目,在员工管理模块中,测试增加/更新员工信息功能,然后通过debug 或者 直接查询数据库数据变更的形式,看看我们在新增/修改数据时,这些公共字段数据是否能够完成自动填充。

1.3 功能完善


1.3.1 思路分析


前面我们已经完成了公共字段自动填充功能的代码开发,但是还有一个问题没有解决,就是我们在自动填充createUser和updateUser时设置的用户id是固定值,现在我们需要完善,改造成动态获取当前登录用户的id。

大家可能想到,用户登录成功后我们将用户id存入了HttpSession中,现在我从HttpSession中获取不就行了?

image.png

注意,我们在MyMetaObjectHandler类中是不能直接获得HttpSession对象的,所以我们需要通过其他方式来获取登录用户id。

那么我先搞清楚一点,当我们在修改员工信息时, 我们业务的执行流程是什么样子的,如下图:

image.png

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

1). LoginCheckFilter的doFilter方法

2). EmployeeController的update方法

3). MyMetaObjectHandler的updateFill方法

我们可以在上述类的方法中加入如下代码(获取当前线程ID,并输出):

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#008855">long</span> <span style="color:#000000">id</span> <span style="color:#981a1a">=</span> <span style="color:#000000">Thread</span>.<span style="color:#000000">currentThread</span>().<span style="color:#000000">getId</span>();
<span style="color:#000000">log</span>.<span style="color:#000000">info</span>(<span style="color:#aa1111">"线程id为:{}"</span>,<span style="color:#000000">id</span>);</span></span>

经过上述的分析之后,发现我们可以使用JDK提供的一个类, 来解决此问题,它是JDK中提供的 ThreadLocal。

1.3.2 ThreadLocal


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

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

ThreadLocal常用方法:

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

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

C. public void remove() : 删除当前线程所对应的线程局部变量的值

我们可以在LoginCheckFilter的doFilter方法中获取当前登录用户id,并调用ThreadLocal的set方法来设置当前线程的线程局部变量的值(用户id),然后在MyMetaObjectHandler的updateFill方法中调用ThreadLocal的get方法来获得当前线程所对应的线程局部变量的值(用户id)。 如果在后续的操作中, 我们需要在Controller / Service中要使用当前登录用户的ID, 可以直接从ThreadLocal直接获取。

1.3.3 操作步骤


实现步骤:

1). 编写BaseContext工具类,基于ThreadLocal封装的工具类

2). 在LoginCheckFilter的doFilter方法中调用BaseContext来设置当前登录用户的id

3). 在MyMetaObjectHandler的方法中调用BaseContext获取登录用户的id

1.3.4 代码实现


1). BaseContext工具类

所属包: com.itheima.reggie.common

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#aa5500">/**</span>
 <span style="color:#aa5500">* 基于ThreadLocal封装工具类,用户保存和获取当前登录用户id</span>
 <span style="color:#aa5500">*/</span>
<span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">BaseContext</span> {
    <span style="color:#770088">private</span> <span style="color:#770088">static</span> <span style="color:#000000">ThreadLocal</span><span style="color:#981a1a"><</span><span style="color:#008855">Long</span><span style="color:#981a1a">></span> <span style="color:#000000">threadLocal</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">ThreadLocal</span><span style="color:#981a1a"><></span>();
    <span style="color:#aa5500">/**</span>
     <span style="color:#aa5500">* 设置值</span>
     <span style="color:#aa5500">* @param id</span>
     <span style="color:#aa5500">*/</span>
    <span style="color:#770088">public</span> <span style="color:#770088">static</span> <span style="color:#008855">void</span> <span style="color:#000000">setCurrentId</span>(<span style="color:#008855">Long</span> <span style="color:#000000">id</span>){
        <span style="color:#000000">threadLocal</span>.<span style="color:#000000">set</span>(<span style="color:#000000">id</span>);
    }
    <span style="color:#aa5500">/**</span>
     <span style="color:#aa5500">* 获取值</span>
     <span style="color:#aa5500">* @return</span>
     <span style="color:#aa5500">*/</span>
    <span style="color:#770088">public</span> <span style="color:#770088">static</span> <span style="color:#008855">Long</span> <span style="color:#000000">getCurrentId</span>(){
        <span style="color:#770088">return</span> <span style="color:#000000">threadLocal</span>.<span style="color:#000000">get</span>();
    }
}</span></span>

2).LoginCheckFilter中存放当前登录用户到ThreadLocal

在doFilter方法中, 判定用户是否登录, 如果用户登录, 在放行之前, 获取HttpSession中的登录用户信息, 调用BaseContext的setCurrentId方法将当前登录用户ID存入ThreadLocal。

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#008855">Long</span> <span style="color:#000000">empId</span> <span style="color:#981a1a">=</span> (<span style="color:#008855">Long</span>) <span style="color:#000000">request</span>.<span style="color:#000000">getSession</span>().<span style="color:#000000">getAttribute</span>(<span style="color:#aa1111">"employee"</span>);
<span style="color:#000000">BaseContext</span>.<span style="color:#000000">setCurrentId</span>(<span style="color:#000000">empId</span>);</span></span>

image.png

相关文章
|
监控 前端开发 测试技术
【瑞吉外卖】day05:增、删、改、查分类以及公共字段自动填充(三)
【瑞吉外卖】day05:增、删、改、查分类以及公共字段自动填充
299 0
【瑞吉外卖】day05:增、删、改、查分类以及公共字段自动填充(三)
|
JSON 前端开发 JavaScript
【瑞吉外卖】day05:增、删、改、查分类以及公共字段自动填充(二)
【瑞吉外卖】day05:增、删、改、查分类以及公共字段自动填充
149 0
【瑞吉外卖】day05:增、删、改、查分类以及公共字段自动填充(二)
22、【收货地址管理模块】——收货地址增、删、改、查、分页列表、地址详情的功能开发
1、接口开发: 新建ShippingController类 image.png 在类上添加相关注解 @Controller @RequestMapping("/shipping/") public class ShippingContro...
2779 0
|
7月前
|
存储 JavaScript
任务清单小功能的实现(任务的增、删、改、查、存储)使用Vue实现
这篇文章介绍了使用Vue实现的任务清单小功能,包括任务的增加、删除、修改、查看以及数据存储。文章通过视频演示展示了实现效果,重点讲解了编辑功能的实现,并通过代码实例展示了编辑功能的核心代码,同时提供了完整的代码下载链接以及以往练习的概览。
任务清单小功能的实现(任务的增、删、改、查、存储)使用Vue实现
|
C语言
c语言学生成绩管理系统(增、删、查、改、排序、分析优秀及格率等)(一)
c语言学生成绩管理系统(增、删、查、改、排序、分析优秀及格率等)详细内容 一、功能描述 实现学生基本信息的管理,包括学生资料的录入、查询、修改、删除、浏览等操作,实现学生成绩信息的管理,包括学生成绩的录入、查询、修改、删除、浏览等操作。学生的基本信息包括:学号、姓名、班级、所选科目等,程序应提供班级与科目信息的录入功能。分析每个班单科成绩。
777 0
c语言学生成绩管理系统(增、删、查、改、排序、分析优秀及格率等)(一)
|
Java 数据库连接 Apache
mybatis学习教程(二)初级的增、删、查、改
引言 本文主要从一个基础实例,讲解Mybatis的实现,已经每一步的详细讲解。我会将项目共享在百度云盘,文章最后! 1、项目结构 2、项目配置  2.1 配置SqlMapConfig.xml         根据Mybatis架构图, 我们第一步应该配置SqlMapConfig,主要涉及到数据源的一些配置。
1020 0
|
关系型数据库 MySQL 数据库
mysql数据库知识点:项目中增、删、改、查应该注意的地方
mysql数据库知识点:项目中增、删、改、查应该注意的地方
241 0
|
9月前
|
XML Java 数据库连接
如何使用 MyBatis 来进行增、删、改、查操作
如何使用 MyBatis 来进行增、删、改、查操作
399 2