本文已参与「新人创作礼」活动,一起开启掘金创作之路。
今天在学习的时候,无意间了解到了mybatis-plus能够对数据库表的字段进行自动填充。在此做一个总结。
1.问题引入
我们的数据库表里面有些字段是相对固定的,比如更新时间,插入时间等。
我们每次修改数据,插入数据的时候都需要设置这些字段,这让我们觉得很烦。接下来,我向大家介绍mybatis-plus的解决方法。
2.解决办法
- 创建一个类,让这个类实现MetaObjectHandler
- 这个类用@Component标注
- 实现insertFill方法,这个方法在你进行插入操作的时候被触发,所以你可以在这个方法里设置公共字段的值。
- 实现updateFill方法,这个方法在你进行更新操作的时候被触发,所以你可以在这个方法里设置公共字段的值。
- insertFill和updateFill设置值的方式是利用MetaObject对象去调用setValue方法。setValue方法有2个参数,第一个参数是数据库字段的名字,第二个参数是写入数据库表的值。
- 找到对应数据库表的实体类,在代表公共字段的属性上,加上注解@TableField(fill = FieldFill.INSERT)插入时填充字段,@TableField(fill = FieldFill.INSERT_UPDATE) 插入和更新时填充字段
实现MetaObjectHandler的类
@Component
@Slf4j
public class MyMetaObjecthandler implements MetaObjectHandler {
/**
* 插入操作,自动填充
* @param metaObject
*/
@Override
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
public void updateFill(MetaObject metaObject) {
log.info("公共字段自动填充[update]");
log.info(metaObject.toString());
long id = Thread.currentThread().getId();
log.info("实现MetaObjectHandler,线程id为:{}",id);
metaObject.setValue("updateTime", LocalDateTime.now());
metaObject.setValue("updateUser", BaseContext.getCurrentId());
}
}
实体类
@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;
}
经过上面的步骤,大家就可以实现公共字段的自动填充,以后只需要做别的数据的crud即可。*这时可能有同学会觉得有点麻烦,比如插入时间可以在mysql数据库里面设置timestamp?
如果只是时间的话,那其实可以不用这么麻烦的,直接设置timestamp,要是公共字段里面除了时间之外,还要求你设置更新人或者创建人呢?是不是用这种方式更加的方便,写一次就够了。
3.插入更新人或者创建人信息的思路
可能有同学有疑问,怎么在实现MetaObjectHandler的类里面获取更新人或者创建人的信息,从而进行设置? 我的思路如下:
- 用ThreadLocal封装用户id(我们在数据库表里一般用id表明用户)
- 在实现MetaObjectHandler的类里获取封装的id
- 进行设置
为什么可以这样呢?因为你发起的同一个请求它触发的进程都是同一个。你可以在每个部分打印他们的id,如下图所示:
上面实现MetaObjectHandler的类里面的BaseContext.getCurrentId(),其实是封装的ThreadLocal的方法。具体代码如下:
public class BaseContext {
//这个类有泛型,因为id是Long,所以这里设置的泛型是Long
private static ThreadLocal<Long> threadLocal = new ThreadLocal<>();
//修饰id
public static void setCurrentId(Long id) {
threadLocal.set(id);
}
//取出修饰的id
public static Long getCurrentId() {
return threadLocal.get();
}
}
接着我在登录的过滤器里面用ThreadLocal修饰用户id(各位同学不一定要在这里设置,看你自己的业务情况)
//4.判断登录状态,如果已登录,则直接放行
if (request.getSession().getAttribute("employee") != null){
log.info("用户已登录,用户id为:{}",request.getSession().getAttribute("employee"));
//用ThreadLocal修饰用户id
Long empId = (Long)request.getSession().getAttribute("employee");
BaseContext.setCurrentId(empId);
long id = Thread.currentThread().getId();
log.info("过滤器中,线程id为:{}",id);
filterChain.doFilter(request,response);
return;
}
最后,在实现MetaObjectHandler的类里面取出这个id即可。