目录
1.在实体类属性上加入@TableField注解,在注解中设置属性,指定自动填充的策略
2.按照mybatis plus框架要求编写元数据对象处理器(一个类)
1.ThreadLocal并不是一个线程(Thread),而是线程(Thread)的局部变量
四、通过ThreadLocal获取创建用户和更新用户的实现步骤
1.编写BaseContext工具类,基于ThreadLocal封装的工具类
3.在MyMetaobjectHandler的方法中调用BaseContext获取登录用户的id
4.代码:元数据对象处理器(MyMetaObjecthandler)
一、问题分析
1.多次编写相同的字段赋值代码,会让代码杂糅
当我们进行系统的某些管理功能时,比如员工管理中的新增员工,虽然前台页面没有显示某些字段,但数据库表中存在的一些不为空字段仍然需要我们赋值。因此就需要我们在controller中设置创建时间、创建人、修改时间、修改人等字段;员工管理中的编辑员工,需要设置修改时间和修改人等字段。这些字段属于公共字段,也就是很多数据库表中都有这些字段,很多功能中都会用到这些字段。如果功能太多,然后又在每个功能中都编写相同的赋值代码,会让代码杂糅。
2.考虑将公共字段放在某个地方统一处理,来简化开发
使用Mybatis Plus提供的公共字段自动填充功能,在插入或更新的时候为指定字段赋予指定的值,统一对这些字段进行处理。
controller中新增员工的代码:
/**
* 新增员工
*
* @param employee 新增的员工信息
* @return 结果信息
*/
@PostMapping
//@RequestBody:将json格式的数据封装到Employee对象中
public R<String> save(HttpServletRequest request, @RequestBody Employee employee) {
log.info("新增员工,员工信息:{}", employee.toString());
//设置初始密码123456,需要进行md5加密处理
employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));
//LocalDateTime.now()获取当前系统时间
employee.setCreateTime(LocalDateTime.now()); //设置创建时间
employee.setUpdateTime(LocalDateTime.now()); //设置更新时间
//获取当前登录用户
Long empId = (Long) request.getSession().getAttribute("employee");
//获取登录用户的id
employee.setCreateUser(empId); //设置创建人
employee.setUpdateUser(empId); //设置更新人
employeeService.save(employee);
return R.success("新增员工成功");
}
二、实现步骤
1.在实体类属性上加入@TableField注解,在注解中设置属性,指定自动填充的策略
(1)哪些属性是公共字段,就在哪些属性上加上该注解,
(2)填充的策略:FieldFill(java提供的api,是一个枚举)
(3)代码:员工实体类
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 员工实体类
*/
@Data
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private Long id; //主键
private String username; //用户名
private String name; //姓名
private String password; //密码
private String phone; //电话
private String sex; //性别
private String idNumber; //身份证号码,驼峰命名法
private Integer status; //账号状态
@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; //更新用户
}
2.按照mybatis plus框架要求编写元数据对象处理器(一个类)
在此类中统一为公共字段赋值,此类需要实现MetaObjectHandler接口。写了此类后就可以删掉controller中的创建时间、创建人、更新时间、更新人等字段的赋值代码。
(1)创建元数据对象处理器(MyMetaObjecthandler),类名随便取,实现MetaObjectHandler接口,重写insertFill()和updateFill()方法。
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
/**
* 自定义元数据对象处理器
*/
@Component //让它成为sping的一个bean,交给spring框架管理
@Slf4j //记录日志
public class MyMetaObjecthandler implements MetaObjectHandler {
/**
* 插入操作,自动填充
* @param metaObject
*/
@Override //项目启动阶段不会执行,执行insert操作的时候执行
public void insertFill(MetaObject metaObject) {
}
/**
* 更新操作,自动填充
* @param metaObject
*/
@Override //项目启动阶段不会执行,执行update操作的时候执行
public void updateFill(MetaObject metaObject) {
}
}
(2)在insertFill()和updateFill()方法中对公共字段赋值
1)时间可以通过LocalDateTime.now()赋值
2)创建用户和更新用户从哪里获得?
①当用户登录成功后我们将用户id存入了HttpSession中,但是我们在MyMetaObjecthandler类中是不能获得HttpSession对象的,所以我们需要通过其他方式来获取登录用户的id。
②可以使用ThreadLocal(线程相关类)来解决此问题,它是jdk中提供的一个类
③客户端发送的每次http请求,对应的在服务端都会分配一个新的线程来处理, 在处理过程中所涉及到这次请求的相关类和相关方法都属于相同的一个线程
三、什么是ThreadLocal?
1.ThreadLocal并不是一个线程(Thread),而是线程(Thread)的局部变量
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。
2.ThreadLocal常用方法
- public void set(T value) : 设置当前线程的线程局部变量的值
- public T get() : 返回当前线程所对应的线程局部变量的值
3.获取当前登录用户id
我们可以在LoginCheckFilter(过滤器)的doFilter方法中获取当前登录用户id,并调用ThreadLocal的set方法来设置当前线程的线程局部变量的值 (用户id),然后MyMetaobjectHandler(元数据对象处理器)的updateFill方法中调用ThreadLocal的get方法来获得当前线程所对应的线程局部变量的值 (用户id)。
四、通过ThreadLocal获取创建用户和更新用户的实现步骤
1.编写BaseContext工具类,基于ThreadLocal封装的工具类
/**
* 基于ThreadLocal封装工具类,用户保存和获取当前登录用户id
*/
public class BaseContext {
// ThreadLocal<Long>中的泛型与实体类中id属性的类型一致
// 泛型为Long是因为实体类中的id属性为Long
private static ThreadLocal<Long> threadLocal = new ThreadLocal<>();
/**
* 设置用户id的值
* @param id
*/
public static void setCurrentId(Long id){
threadLocal.set(id);
}
/**
* 获取用户id的值
* @return
*/
public static Long getCurrentId(){
return threadLocal.get();
}
}
2.设置当前登录用户id
在过滤器LoginCheckFilter的doFilter方法中调用BaseContext来设置当前登录用户的id(或者在能拿到session的类中)
代码:过滤器中新增的代码
//强转为Long型,因为实体类中id属性为Long型
Long empId = (Long)request.getSession().getAttribute("employee");
//调用刚刚写的BaseContext类,将从session中获取的用户id值传给工具类BaseContext
BaseContext.setCurrentId(empId);
3.在MyMetaobjectHandler的方法中调用BaseContext获取登录用户的id
4.代码:元数据对象处理器(MyMetaObjecthandler)
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 //让它成为sping的一个bean,交给spring框架管理
@Slf4j //记录日志
public class MyMetaObjecthandler implements MetaObjectHandler {
/**
* 插入操作,自动填充
* @param metaObject
*/
@Override //项目启动阶段不会执行,执行insert操作的时候执行
public void insertFill(MetaObject metaObject) {
log.info("公共字段自动填充[insert]……");
log.info(metaObject.toString());
//设置属性名和属性值,属性名与实体类中属性名一一对应
metaObject.setValue("createTime",LocalDateTime.now()); //创建时间
metaObject.setValue("updateTime",LocalDateTime.now()); //更新时间
//先设置固定值
metaObject.setValue("createUser",BaseContext.getCurrentId()); //创建用户
metaObject.setValue("updateUser",BaseContext.getCurrentId()); //更新用户
}
/**
* 更新操作,自动填充
* @param metaObject
*/
@Override //项目启动阶段不会执行,执行update操作的时候执行
public void updateFill(MetaObject metaObject) {
log.info("公共字段自动填充[update]……");
log.info(metaObject.toString());
metaObject.setValue("createTime",LocalDateTime.now());
metaObject.setValue("updateTime",LocalDateTime.now());
}
}