通过mybatis拦截器实现新增,修改数据时,自动添加操作人,操作时间等信息,从而不用在业务代码中关心这些问题。
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import java.lang.reflect.InvocationTargetException;
import java.util.Date;
import java.util.Map;
import java.util.Properties;
/**
* @author james
* @description 给各实体对象在增加、修改时,自动添加操作属性信息
*/
@Slf4j
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
public class OperationInfoInterceptor implements Interceptor {
@Override
public Object intercept(final Invocation invocation) throws Throwable {
final Object[] args = invocation.getArgs();
// update方法有两个参数,参见Executor类中的update()方法。
final Object arg0 = args[0];
final Object arg1 = args[1];
// 第一个参数处理。根据它判断是否给“操作属性”赋值。
if (arg0 instanceof MappedStatement) {// 如果是第一个参数 MappedStatement
final MappedStatement mappedStatement = (MappedStatement) arg0;
final SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
if (sqlCommandType == SqlCommandType.INSERT || sqlCommandType == SqlCommandType.UPDATE) {
// 如果是“增加”或“更新”操作,则继续进行默认操作信息赋值。否则,则退出
if (arg1 instanceof Map) {// 如果是map,有两种情况:(1)使用@Param多参数传入,由Mybatis包装成map。(2)原始传入Map
final Map map = (Map) arg1;
for (final Object obj : map.values()) {
setProperty(obj, sqlCommandType);
}
} else {// 原始参数传入
setProperty(arg1, sqlCommandType);
}
}
}
return invocation.proceed();
}
/**
* 为对象的操作属性赋值
*
* @param obj
*/
private void setProperty(final Object obj, final SqlCommandType sqlCommandType) {
if (!(obj instanceof CommonEntity)) {
return;
}
//从用户上下文获取当前登录用户
final User currentUser = UserContext.getCurrentUser();
try {
if (sqlCommandType == SqlCommandType.INSERT) {
BeanUtils.setProperty(obj, "id", UuidGenerator.generate());
BeanUtils.setProperty(obj, "createdById", currentUser != null ? currentUser.getId() : null);
BeanUtils.setProperty(obj, "createdTime", new Date());
BeanUtils.setProperty(obj, "updatedById", currentUser != null ? currentUser.getId() : null);
BeanUtils.setProperty(obj, "updatedTime", new Date());
} else {
BeanUtils.setProperty(obj, "updatedById", currentUser != null ? currentUser.getId() : null);
BeanUtils.setProperty(obj, "updatedTime", new Date());
}
} catch (final IllegalAccessException | InvocationTargetException e) {
log.error(e.getMessage(), e);
}
}
@Override
public Object plugin(final Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(final Properties properties) {
}
}
以下是CommonEntity的代码:
@Data
public abstract class CommonEntity {
private String id;
private Date createdTime;
private String createdById;
private String updatedById;
private Date updatedTime;
}
用户上下文代码如下:
public final class UserContext {
private static final ThreadLocal<User> CURRENT_USER = new ThreadLocal<>();
public static User getCurrentUser() {
return CURRENT_USER.get();
}
public static void close() {
CURRENT_USER.remove();
}
}
那么这个用户上下文是如何打开的呢?网关拦截器获取token,然后通过token从redis获取token对应的用户信息,然后放入用户请求的上下文。具体内容看这里:https://mp.youkuaiyun.com/postedit/100150760