在实际项目开发中,我们可能需要在mapper层插入相应的数据,而这些数据在各个表基本都有,比如
创建时间,更新时间,创建人,更新人这些,但是又不想在每个业务中都去设置这些值,那么我们就可以使用mybatis拦截器实现数据自动填充。

一、如何实现?
1.首先添加mybatis相关依赖。

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.1</version>
        </dependency>

2.自定义mybatis拦截器。

import cn.hutool.core.date.DateUtil;
import lombok.extern.slf4j.Slf4j;
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.apache.ibatis.session.defaults.DefaultSqlSession.StrictMap;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.*;
@Slf4j
@Component
@Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) })
public class MybatisInterceptor implements Interceptor {

    private static  final String DATETYPE="java.util.Date";
    private static  final String STRINGTYPE="java.lang.String";

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        // 获取sql执行类型:insert、update、select、delete
        SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
        Object parameter = invocation.getArgs()[1];
        if (parameter == null) {
            return invocation.proceed();
        }
        //获取当前登录用户id
        String userId = TokenUtil.getConcurrentUserId();
        log.debug(" userId is {}....",userId);
       // 当sql为新增或更新类型时,自动填充操作人相关信息
        if (SqlCommandType.INSERT == sqlCommandType || SqlCommandType.UPDATE == sqlCommandType) {
            replaceEntityProperty(parameter,userId,sqlCommandType);
        }
        return invocation.proceed();
    }
    private void replaceEntityProperty(Object parameter,String userId, SqlCommandType sqlCommandType ) {
        // StrictMap
        if (parameter instanceof StrictMap) {
            replaceStrictMap((StrictMap) parameter,userId,sqlCommandType);
        } else if (parameter instanceof Map) {
            replaceMap((Map) parameter,userId,sqlCommandType);
        } else {
            replace(parameter,userId,sqlCommandType);
        }
    }
    private void replace(Object parameter,String userId,SqlCommandType sqlCommandType ) {
        if(SqlCommandType.INSERT == sqlCommandType){
            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 ("deleted".equals(field.getName())) {
                        field.set(parameter, Integer.valueOf("0"));
                        field.setAccessible(false);
                    }else if ("createdBy".equals(field.getName())) {
                        field.set(parameter, userId);
                        field.setAccessible(false);
                   }else if ("createdTime".equals(field.getName())) {
                        String type = field.getType().getName();
                        if(DATETYPE.equals(type)){
                            field.set(parameter, new Date());
                        }else if(STRINGTYPE.equals(type)) {
                            field.set(parameter, DateUtil.formatDateTime(new Date()));
                        }
                        field.setAccessible(false);
                    }else {
                        updateProperty(parameter, field, userId);
                    }
                } catch (Exception e) {
                    log.error("failed to insert data, exception = ", e);
                }
            }
        }else if(SqlCommandType.UPDATE == sqlCommandType){
            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;
                    }
                    //更新时只判断是否更新的属性
                    updateProperty(parameter, field, userId);
                }catch (Exception e){
                    log.error("failed to update data, exception = ", e);
                }
            }
        }
    }
    private void replaceStrictMap(StrictMap map, String userId, SqlCommandType sqlCommandType ) {
        if (map.containsKey("collection")) {
            Object collection = map.get("collection");
            for (Object t : (Collection) collection) {
                replace(t,userId,sqlCommandType);
            }
        } else if (map.containsKey("array")) {
            Object collection = map.get("array");
            for (Object t : (Object[]) collection) {
                replace(t,userId,sqlCommandType);
            }
        }
    }
    private void replaceMap(Map map,String userId, SqlCommandType sqlCommandType) {
        for (Object value : map.values()) {
            replace(value,userId,sqlCommandType);
        }
    }

    private void updateProperty(Object parameter, Field field, String userId) throws IllegalAccessException {
        if ("updatedBy".equals(field.getName())) {
            field.set(parameter, userId);
            field.setAccessible(false);
        }else if ("updatedTime".equals(field.getName())) {
            String type = field.getType().getName();
            if (DATETYPE.equals(type)) {
                field.set(parameter, new Date());
            } else if (STRINGTYPE.equals(type)) {
                field.set(parameter, DateUtil.formatDateTime(new Date()));
            }
            field.setAccessible(false);
        }else {
            //其他属性什么也不做
            field.setAccessible(false);
        }
    }
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }

    /**
     * 获取类的所有属性,包括父类
     */
    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;
    }
}

下面是TokenUtil类,用于获取当前登录用户id。

@Slf4j
public class TokenUtil {
	public static String getConcurrentUserId() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        if (Objects.isNull(requestAttributes)) {
            return "";
        } else {
            HttpServletRequest request = requestAttributes.getRequest();
            String userId = request.getHeader("x-userId");
            if (!StringUtils.isBlank(userId)) {
                return userId;
            } else {
                Object attribute = request.getAttribute("x-userId");
                return Objects.nonNull(attribute) ? attribute.toString() : "";
            }
        }
    }
}

这样就实现了在DAO层 创建人等相关信息自动填充。

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐