一、背景
项目中,我们的插入,修改数据的时候,对于公共字段,创建人、创建时间、逻辑删除状态等,如果是每一个对象里面赋值,就比较多余,造成不必要的工作量。而且需要每次都要去赋值,其实项目中是挺麻烦的,这样就不能只关注业务。由此我将引入MybatisPuls,通过拦截器实现自动填写公共字段。
二、代码解释
1、依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.0</version>
</dependency>
2、注解
@Intercepts({@Signature(type = Executor.class, method = "update", args = {
MappedStatement.class, Object.class
})})
这段代码是用于创建一个AOP(面向切面编程)拦截器,用于拦截Executor
接口的update
方法。AOP是一种设计模式,用于在不修改原有代码的情况下,为现有代码添加额外的功能。
intercepts属性:指定要拦截的接口和方法。在这个例子中,我们 intercepts 了一个名为Executor
的接口,以及该接口中的update
方法。
signature属性:用于指定要拦截的方法的参数类型和参数名称。在这个例子中,update
方法的参数类型为MappedStatement
和Object
,参数名称分别为mappedStatement
和parameter
。
注意:这段代码需要与相应的切面类一起使用,以便在实际项目中生效。
2、重写 Interceptor
public interface Interceptor {
Object intercept(Invocation var1) throws Throwable;
default Object plugin(Object target) {
return Plugin.wrap(target, this);
}
default void setProperties(Properties properties) {
}
}
3、完整代码
package com.jrlyt.common.interceptor;
import com.jrlyt.common.until.LoginUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.util.*;
/**
* 填充createBy,createTime等公共字段的拦截器
*
* @author JRLYT9
* @version 1.0
* @date 2024-06-12 23:45:23
*/
@Component
@Slf4j
@Intercepts({@Signature(type = Executor.class, method = "update", args = {
MappedStatement.class, Object.class
})})
public class MybatisInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
//获取sql类型
SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
//获取参数
Object parameter = invocation.getArgs()[1];
if (parameter == null) {
return invocation.proceed();
}
//获取当前登录用户的id
String loginId = LoginUtil.getLoginId();
if (StringUtils.isBlank(loginId)) {
//允许当前方法的执行继续进行,而不会被中断。这在使用异常处理时非常有用,因为它允许程序在遇到错误时继续执行,而不是立即抛出异常。
return invocation.proceed();
}
//填充公共字段(插入、修改)
if (SqlCommandType.INSERT == sqlCommandType || SqlCommandType.UPDATE == sqlCommandType) {
replaceEntityProperty(parameter, loginId, sqlCommandType);
}
return invocation.proceed();
}
private void replaceEntityProperty(Object parameter, String loginId, SqlCommandType sqlCommandType) {
//instanceof的实现原理是检查对象的类实例是否与ClassName匹配。如果匹配,那么结果为true,否则为false
if (parameter instanceof Map) {
replaceMap((Map) parameter, loginId, sqlCommandType);
} else {
replace(parameter, loginId, sqlCommandType);
}
}
private void replaceMap(Map parameter, String loginId, SqlCommandType sqlCommandType) {
for (Object val : parameter.values()) {
replace(val, loginId, sqlCommandType);
}
}
private void replace(Object parameter, String loginId, SqlCommandType sqlCommandType) {
if (SqlCommandType.INSERT == sqlCommandType) {
dealInsert(parameter, loginId);
} else {
dealUpdate(parameter, loginId);
}
}
private void dealUpdate(Object parameter, String loginId) {
Field[] fields = getAllFields(parameter);
for (Field field : fields) {
try {
field.setAccessible(true);
Object o = field.get(parameter);
if (Objects.nonNull(o)) {
field.setAccessible(false);
continue;
}
if ("updateBy".equals(field.getName())) {
field.set(parameter, loginId);
field.setAccessible(false);
} else if ("updateTime".equals(field.getName())) {
field.set(parameter, new Date());
field.setAccessible(false);
} else {
field.setAccessible(false);
}
} catch (Exception e) {
log.error("dealUpdate.error:{}", e.getMessage(), e);
}
}
}
private void dealInsert(Object parameter, String loginId) {
Field[] fields = getAllFields(parameter);
for (Field field : fields) {
try {
field.setAccessible(true);
Object o = field.get(parameter);
if (Objects.nonNull(o)) {
//在需要保护类内部变量不被外部直接访问时,可以使用这个方法。例如,在密码加密、用户身份验证等场景中
field.setAccessible(false);
continue;
}
if ("isDeleted".equals(field.getName())) {
field.set(parameter, 0);
field.setAccessible(false);
} else if ("createdBy".equals(field.getName())) {
field.set(parameter, loginId);
field.setAccessible(false);
} else if ("createdTime".equals(field.getName())) {
field.set(parameter, new Date());
field.setAccessible(false);
} else {
field.setAccessible(false);
}
} catch (Exception e) {
log.error("dealInsert.error:{}", e.getMessage(), e);
}
}
}
/**
* 获取字段
* @param object
* @return
*/
private Field[] getAllFields(Object object) {
//反射获取对象
Class<?> clazz = object.getClass();
List<Field> fieldList = new ArrayList<>();
while (clazz != null) {
//获取类的所有字段
fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
clazz = clazz.getSuperclass();
}
//定义数组
Field[] fields = new Field[fieldList.size()];
fieldList.toArray(fields);
return fields;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}