Mybatis拦截器实现操作日志记录

        大家好,今天给大家带来一篇基于Mybatis拦截器实现的操作日志记录的解决的方案。大家可能会说,切,操作日志记录这么简单的东西,老生常谈了。不!

网上的操作日志一般就是记录操作人,操作的描述,ip等。好一点的增加了修改的数据和执行时间。那么!我这篇有什么不同呢!今天这种不仅可以记录上方所说的一切,还增加多表记录了操作前的数据

思路介绍:

        记录操作日志的操作前数据是需要思考的重点。我们以修改场景来作为探讨。当我们要完全记录数据的流向的时候,我们必然要记录修改前的数据,而前台进行提交的时候,只有修改的数据,那么如何找到修改前的数据呢。有三个大的要素,我们需要知道修改前数据的表名,表的字段主键,表主键的值。这样通过这三个属性,我们可以很容易的拼出 select * from 表名 where 主键字段 = 主键值。我们就获得了修改前的数据,转换为json之后就可以存入到数据库中了。如何获取三个属性就是重中之重了。我们采取的方案是通过mybati拦截器拦截执行的sql,根据 Java 的反射取到值。

1.pom文件

        本次是基于idea创建spring boot 项目,不会的同学请自行百度,本次就不在这里赘述。

  <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.3.6</version>
        </dependency>

        <!--spring依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.46</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.9.2</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.2</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.69</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.4</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.2</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-collections4</artifactId>
            <version>4.1</version>
        </dependency>

2.mybatis拦截器获取sql

  • 像springmvc一样,mybatis也提供了拦截器实现,只是他们拦截的对象不同。
  • mybatis给Executor、StatementHandler、ResultSetHandler、ParameterHandler提供了拦截器功能,
    • Executor提供了增删改查的接口.
    • StatementHandler负责处理Mybatis与JDBC之间Statement的交互.
    • ResultSetHandler负责处理Statement执行后产生的结果集,生成结果列表.
    • ParameterHandler是Mybatis实现Sql入参设置的对象。

import com.alibaba.fastjson.JSON;
import com.example.recordlog.tools.SqlTools;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.Properties;


@Component
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
public class SqlInterceptor implements Interceptor {


    private static final Logger log = LoggerFactory.getLogger(SqlInterceptor.class);


    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        // 获取拦截方法的参数
        Object[] args = invocation.getArgs();
        MappedStatement mappedStatement = (MappedStatement) args[0];
        Configuration configuration = mappedStatement.getConfiguration();
        Object parameter = null;
        if (invocation.getArgs().length > 1) {
            parameter = invocation.getArgs()[1];
        }

        BoundSql boundSql = mappedStatement.getBoundSql(parameter);
 
        // 执行结果
        Object returnValue = invocation.proceed();

        // 获取sql语句
        String sql = SqlTools.showSql(configuration, boundSql);
        System.out.println("sql : " + sql);
      
        return returnValue;
    }


    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }

}

获取操作类型

        为什么要获取操作类型,因为只有在update或delete操作的时候才会有修改前的数据,所以要先获取操作类型,只有操作类型为update或delete操作的时候才会取查询修改前的数据。

 /**
     * 判断sql操作类型 :update和delete的时候才需要去查询修改前的值
     *
     * @param mappedStatement
     * @return java.lang.String
     */
    public static String SqlCommandType(MappedStatement mappedStatement) {

        if (SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) {
            return OperateTypeConstant.SELECT;
        }

        if (SqlCommandType.INSERT.equals(mappedStatement.getSqlCommandType())) {
            return OperateTypeConstant.INSERT;
        }

        if (SqlCommandType.UPDATE.equals(mappedStatement.getSqlCommandType())) {
            return OperateTypeConstant.UPDATE;
        }
        if (SqlCommandType.DELETE.equals(mappedStatement.getSqlCommandType())) {
            return OperateTypeConstant.DELETE;
        }
        return null;
    }

操作类型常量


/**
 * @author liuminglin
 * @desc: 操作常量
 */
public class OperateTypeConstant {
    public static final String SELECT = "select";
    public static final String INSERT = "insert";
    public static final String UPDATE = "update";
    public static final String DELETE = "delete";
    public static final String FLUSH = "flush";

}

动态获取表名

        为什么要动态获取表名? 我们需要知道修改前数据的表名,我这里是通过mybatis拦截器拿到sql,通过截取的方式获取表名,暂时没有更好方法获取表名,如有请告知

 截取规则 :
     1.替换sql命令类型标签(select delete insert update)
     2.替换掉from标签,去除前后空格
     3.截取从0开始,\n结束

 /**
     * 获取表名
     * 截取规则 :
     * 1.替换sql命令类型标签(select delete insert update)
     * 2.替换掉from标签,去除前后空格
     * 3.截取从0开始,\n结束
     */
    public static String getTableName(BoundSql boundSql, String sqlType) {

        if (StringUtils.isBlank(sqlType) || StringUtils.isBlank(boundSql.getSql())) {
            return "";
        }


        //根据sqlType 去除update标签,delete标签,from标签,去除前后空格
        String tableStr = boundSql.getSql().replaceAll(sqlType, "").trim().replaceAll("from", "");
        String tableName = tableStr.substring(0, tableStr.indexOf("\n"));
        return tableName;
    }

动态获取字段名

        为什么要获取字段名? 我们需要知道修改前数据表的字段名(也就是主键名称),我这里是通过mybatis拦截器拿到sql,通过截取的方式获取字段名,

截取规则 :
     1  去掉   \n     ? =
     2.从where索引位置开始截取, 到sqlStr最后一个位置 ,替换掉where操作符,去除空格
 

 public static String getFieldName(BoundSql boundSql, String sqlType) {

        if (StringUtils.isBlank(sqlType) || StringUtils.isBlank(boundSql.getSql())) {
            return "";
        }
        log.info("getSql :{}", JSON.toJSONString(boundSql.getSql()));

        String sqlStr = boundSql.getSql().replaceAll("\n", "").replaceAll("\\?", "").replaceAll("=", "");
        String fieldName = sqlStr.substring(sqlStr.indexOf("where"), sqlStr.length()).replace("where", "").trim();


        return fieldName;
    }

批量解析

思路,批量修改和删除会导致sql不一样,所以首先批量解析之前要先判断是否是批量,如果是参数批量就需要单独解析,如何判断是批量呢?

如果是批量参数,mapper接口会使用 @parm注解,参数是list,mapper.xml文件中要使用foreach标签进行遍历,所以我们要判断拦截的参数是否包含parm注解就可以了。

(1)用@Param注解标记list参数

mapper接口@Param标记
Integer batchUpdateInfo( @Param("userInfoList") List<UserInfo> userInfoList);

(2)mapper.xml使用foreach标签遍历

<!-- mapper.xml使用foreach标签遍历   -->
    <update id="batchUpdateInfo" parameterType="java.util.List">
        <foreach collection="userInfoList" item="item" index="index" open="" close="" separator=";">
            update user_info
            <set>
                <if test="item.userId != null ">
                    user_Id = #{item.userId,jdbcType=VARCHAR},
                </if>
                <if test="item.userName != null">
                    user_name = #{item.userName,jdbcType=VARCHAR},
                </if>

            </set>
            where id = #{item.id,jdbcType=BIGINT}
        </foreach>
    </update>

(3)获取批量标记

 /**
     * 获取批次标记
     *
     * @param parameter
     * @return boolen
     */
    private static boolean getbatchFlag(Object parameter) {

        if (null != parameter && parameter.toString().contains("param")) {
            return true;
        }
        return false;
    }

(4)批量解析:

        批量解析只需要在原有的方法是判断是否批量,如果是批量的话进一步解析,batchFlag=true代表批量,需要从新解析(截取fieldName,从beginIndex=0开始截取,endIndex=;结束

 /**
     * 获取sql获取FieldName
     * 截取规则 :
     * (1)去掉\n ,?,=
     * (2)从where索引位置开始截取, 到sqlStr最后一个位置 ,替换掉where操作符,去除空格
     * (3) batchFlag=true代表批量,需要从新解析(截取fieldName,从beginIndex=0开始截取,endIndex=;结束
     */
    private static String getFieldName(BoundSql boundSql, String sqlType, boolean batchFlag) {

        if (StringUtils.isBlank(sqlType) || StringUtils.isBlank(boundSql.getSql())) {
            return "";
        }

        String sqlStr = boundSql.getSql().replaceAll("\n", "").replaceAll("\\?", "").replaceAll("=", "");
        String fieldName = sqlStr.substring(sqlStr.indexOf("where"), sqlStr.length()).replace("where", "").trim();

        //解析批量
        if (batchFlag) {
            fieldName = fieldName.substring(0, fieldName.indexOf(";")).trim();
        }
        return fieldName;
    }

反射动态获取属性值

思路介绍:

        第一种实现方式,调用对象的get方法获取

        第二种实现方式,通过反射动态获取

 /**
     * 根据属性名获取属性值
     */
    public static Object getFieldValueByName(String fieldName, Object o) {

        if (StringUtils.isBlank(fieldName) || o == null) {
            return null;
        }

        try {
            String firstLetter = fieldName.substring(0, 1).toUpperCase();
            String getter = "get" + firstLetter + fieldName.substring(1);
            Method method = o.getClass().getMethod(getter, new Class[]{});
            Object value = method.invoke(o, new Object[]{});
            return value;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return null;
        }
    }

    /**
     * 根据属性名获取属性值
     *
     * @param fieldName
     * @param object
     * @return
     */
    public static Object getFieldValueByFieldName(String fieldName, Object object) {
        try {
            Field field = object.getClass().getDeclaredField(fieldName);
            //设置对象的访问权限,保证对private的属性的访问
            field.setAccessible(true);
            return  field.get(object);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return null;
        }
    }

批量动态解析属性值

        原来是单个参数,直接通过属性名获取属性值就可以了,批量是多个参数,需要单独解析

上面已经拿到批量标识,batchFlag=true走批量解析逻辑 ,batchFlag=false走单个参数解析,

判断field.getName()的值==fieldName值相等,获取主键值,这样就拿到了所有的主键。
/**
     * 反射获取  @Param注解标记list属性值
     *
     * @param primarykeyName 主键名称
     * @param object         对象
     * @param batchFlag      batchFlag=true批量 ,batchFlag=false代表不是批量操作
     * @return
     */
    private static List<Map> reflectGetListValue(String primarykeyName, Object object, boolean batchFlag) {

        List<Map> resultList = new ArrayList<>();
        if (null == object) {
            return resultList;
        }

        //单个参数解析
        Map argsValueMap = new HashMap();
        if (!batchFlag) {
            Map argsMaps = beanToMap(object);
            for (Object obj : argsMaps.keySet()) {
                if (obj.equals(primarykeyName)) {
                    argsValueMap.put("obj", argsMaps.get(obj));
                }
            }
            resultList.add(argsValueMap);
            return resultList;
        }

        //批量解析
        Map<String, Object> parameterMap = (Map) object;
        for (Map.Entry<String, Object> entery : parameterMap.entrySet()) {
            String key = entery.getKey();
            if (key.indexOf("param") < 0) {
                List<Object> objList = (List) entery.getValue();
                if (objList != null) {
                    for (Object obj : objList) {
                        Map<String, Object> listChild = new HashMap<>();
                        Class<?> clz = obj.getClass();
                        Field[] declaredFields = clz.getDeclaredFields();
                        for (int i = 0; i < declaredFields.length; i++) {
                            Field field = declaredFields[i];
                            field.setAccessible(true);
                            try {
                                //Object value = field.get(obj);
                                if (StringUtils.equals(primarykeyName, field.getName())) {
                                    Object value = field.get(obj);
                                    listChild.put(field.getName(), value);
                                }
                            } catch (IllegalAccessException e) {
                                e.printStackTrace();
                            }
                        }
                        resultList.add(listChild);
                    }
                }
            }
        }
        return resultList;
    }

查询修改前的数据

        有了上面的4个属性,接下来的一步就是对修改前或删除前的数据进行拼接sql。之前我们提到过如果是修改和删除,我们才会进行数据的拼接获取,最终去查询修改前的数据。

通过 jdbcTemplate.queryForList方法查询修改前的数据

jdbcTemplate.queryForList(sql, argsValue);

修改前数据

批量查询修改前的数据

        批量怎么查询呢,上面我们已经拿到所有的主键,直接遍历主键列表,查询修改前的值

List<Map> primarykeyList = SqlTools.reflectGetListValue(primarykeyName, parameter, batchFlag);
        //遍历主键列表
        for (Map map : primarykeyList) {
            for (Object obj : map.keySet()) {
                //获取主键值
                Object primarykeyValue = map.get(obj);
                queryData(jdbcTemplate, tableName, primarykeyName, primarykeyValue, resultList);
            }
        }

2.拦截器的完整实现代码


import com.alibaba.fastjson.JSON;
import com.example.recordlog.tools.SqlTools;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Properties;


@Component
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class,
                Object.class, RowBounds.class, ResultHandler.class})})
public class MybatisInterceptor implements Interceptor {


    private static final Logger log = LoggerFactory.getLogger(MybatisInterceptor.class);

    @Autowired
    private JdbcTemplate jdbcTemplate;


    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        // 获取拦截方法的参数
        Object[] args = invocation.getArgs();
        MappedStatement mappedStatement = (MappedStatement) args[0];
        Configuration configuration = mappedStatement.getConfiguration();
        Object parameter = null;
        if (invocation.getArgs().length > 1) {
            parameter = invocation.getArgs()[1];
        }

        // 获取到节点的id,即sql语句的id
//        String sqlId = mappedStatement.getId();
//        String namespace = sqlId.substring(sqlId.indexOf("c"), sqlId.lastIndexOf("."));
//        MappedStatement mappedStatementa = configuration.getMappedStatement(namespace + "." + "selectByPrimaryKey");
        
        BoundSql boundSql = mappedStatement.getBoundSql(parameter);
        //获取修改器前的值
        List resultMaps = SqlTools.getDataBeforeOperate(mappedStatement, boundSql, parameter, jdbcTemplate);
        // 执行结果
        Object returnValue = invocation.proceed();
        // 获取sql语句
        String sql = SqlTools.showSql(configuration, boundSql);
        log.info("Sql :{}", JSON.toJSONString(sql));
        log.info("修改前数据 :{}", JSON.toJSONString(resultMaps));
        log.info("修改后数据 :{}", JSON.toJSONString(parameter));
        return returnValue;
    }


    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }

3.SqlTools工具类


import com.example.recordlog.constant.OperateTypeConstant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.jdbc.core.JdbcTemplate;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.DateFormat;
import java.util.*;

@Slf4j
public class SqlTools {


    private SqlTools() {
    }


    /**
     * 解析BoundSql,生成不含占位符的SQL语句
     *
     * @param configuration
     * @param boundSql
     * @return java.lang.String
     */
    public static String showSql(Configuration configuration, BoundSql boundSql) {
        Object parameterObject = boundSql.getParameterObject();
        // 获取方法参数
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        // 格式化SQL
        String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
        if (parameterMappings.size() > 0 && parameterObject != null) {
            // 类型解析器
            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
            if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                sql = sql.replaceFirst("\\?", getParameterValue(parameterObject));
            } else {
                MetaObject metaObject = configuration.newMetaObject(parameterObject);
                for (ParameterMapping parameterMapping : parameterMappings) {
                    String propertyName = parameterMapping.getProperty();
                    String[] s = metaObject.getObjectWrapper().getGetterNames();
                    Arrays.toString(s);
                    if (metaObject.hasGetter(propertyName)) {
                        Object obj = metaObject.getValue(propertyName);
                        sql = sql.replaceFirst("\\?", getParameterValue(obj));
                    } else if (boundSql.hasAdditionalParameter(propertyName)) {
                        Object obj = boundSql.getAdditionalParameter(propertyName);
                        sql = sql.replaceFirst("\\?", getParameterValue(obj));
                    }
                }
            }
        }
        return sql;
    }

    /**
     * 若为字符串或者日期类型,则在参数两边添加''
     *
     * @param obj
     * @return java.lang.String
     */
    private static String getParameterValue(Object obj) {
        String value;
        if (obj instanceof String) {
            value = "'" + obj.toString() + "'";
        } else if (obj instanceof Date) {
            DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
            value = "'" + formatter.format(new Date()) + "'";
        } else {
            if (obj != null) {
                value = obj.toString();
            } else {
                value = "";
            }
        }
        return value;
    }


    /**
     * @param operateType 操作类型
     * @param tableName   表名
     * @param fieldName   参数
     * @param argsValue   参数值
     * @return
     * @desc 操作前获取数据(修改或者删除的时候需要查询操作前的数据)
     */
    public static List getDataBeforeOperate(MappedStatement mappedStatement, BoundSql boundSql, Object parameter, JdbcTemplate jdbcTemplate) {

        List resultList = new ArrayList();

        //SELECT和INSERT操作不用记录修改前的值,直接返回
        if (parameter == null || SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())
                || SqlCommandType.INSERT.equals(mappedStatement.getSqlCommandType())) {
            return resultList;

        }

        String operateType = SqlTools.SqlCommandType(mappedStatement);
        boolean batchFlag = SqlTools.getbatchFlag(parameter);
        String tableName = SqlTools.getTableName(boundSql, operateType);
        String primarykeyName = SqlTools.getFieldName(boundSql, operateType, batchFlag);
        // Object argsValue = SqlTools.getFieldValueByFieldName(fieldName, object);

        List<Map> primarykeyList = SqlTools.reflectGetListValue(primarykeyName, parameter, batchFlag);
        //遍历主键列表
        for (Map map : primarykeyList) {
            for (Object obj : map.keySet()) {
                //获取主键值
                Object primarykeyValue = map.get(obj);
                queryData(jdbcTemplate, tableName, primarykeyName, primarykeyValue, resultList);
            }
        }
        return resultList;
    }


    /**
     * 判断sql操作类型 :update和delete的时候才需要去查询修改前的值
     *
     * @param mappedStatement
     * @return java.lang.String
     */
    private static String SqlCommandType(MappedStatement mappedStatement) {

        if (SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) {
            return OperateTypeConstant.SELECT;
        }

        if (SqlCommandType.INSERT.equals(mappedStatement.getSqlCommandType())) {
            return OperateTypeConstant.INSERT;
        }

        if (SqlCommandType.UPDATE.equals(mappedStatement.getSqlCommandType())) {
            return OperateTypeConstant.UPDATE;
        }
        if (SqlCommandType.DELETE.equals(mappedStatement.getSqlCommandType())) {
            return OperateTypeConstant.DELETE;
        }
        return null;
    }

    /**
     * 获取批次标记
     *
     * @param parameter
     * @return boolen
     */
    private static boolean getbatchFlag(Object parameter) {

        if (null != parameter && parameter.toString().contains("param")) {
            return true;
        }
        return false;
    }

    /**
     * 获取表名
     * 截取规则 :
     * 1.替换sql命令类型标签(select delete insert update)
     * 2.替换掉from标签
     * 3.截取从0开始,\n结束
     */
    private static String getTableName(BoundSql boundSql, String sqlType) {

        if (StringUtils.isBlank(sqlType) || StringUtils.isBlank(boundSql.getSql())) {
            return "";
        }

        //根据sqlType 去除update标签,delete标签,from标签,去除前后空格
        String tableStr = boundSql.getSql().replaceAll(sqlType, "").trim().replaceAll("from", "");
        String tableName = tableStr.substring(0, tableStr.indexOf("\n"));

        return tableName;
    }


    /**
     * 获取sql获取FieldName
     * 截取规则 :
     * (1)去掉\n ,?,=
     * (2)从where索引位置开始截取, 到sqlStr最后一个位置 ,替换掉where操作符,去除空格
     * (3) batchFlag=true代表批量,需要从新解析(截取fieldName,从beginIndex=0开始截取,endIndex=;结束
     */
    private static String getFieldName(BoundSql boundSql, String sqlType, boolean batchFlag) {

        if (StringUtils.isBlank(sqlType) || StringUtils.isBlank(boundSql.getSql())) {
            return "";
        }

        String sqlStr = boundSql.getSql().replaceAll("\n", "").replaceAll("\\?", "").replaceAll("=", "");
        String fieldName = sqlStr.substring(sqlStr.indexOf("where"), sqlStr.length()).replace("where", "").trim();

        //解析批量
        if (batchFlag) {
            fieldName = fieldName.substring(0, fieldName.indexOf(";")).trim();
        }
        return fieldName;
    }

    /**
     * 根据属性名获取属性值
     */
    private static Object getFieldValueByName(String fieldName, Object o) {

        if (StringUtils.isBlank(fieldName) || o == null) {
            return null;
        }

        try {
            String firstLetter = fieldName.substring(0, 1).toUpperCase();
            String getter = "get" + firstLetter + fieldName.substring(1);
            Method method = o.getClass().getMethod(getter, new Class[]{});
            Object value = method.invoke(o, new Object[]{});
            return value;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return null;
        }
    }

    /**
     * 根据属性名获取属性值
     *
     * @param fieldName
     * @param object
     * @return
     */
    private static Object getFieldValueByFieldName(String fieldName, Object object) {
        try {
            Field field = object.getClass().getDeclaredField(fieldName);
            //设置对象的访问权限,保证对private的属性的访问
            field.setAccessible(true);
            return field.get(object);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return null;
        }
    }


    /**
     * 反射获取  @Param注解标记list属性值
     *
     * @param primarykeyName 主键名称
     * @param object         对象
     * @param batchFlag      batchFlag=true批量 ,batchFlag=false代表不是批量操作
     * @return
     */
    private static List<Map> reflectGetListValue(String primarykeyName, Object object, boolean batchFlag) {

        List<Map> resultList = new ArrayList<>();
        if (null == object) {
            return resultList;
        }

        //单个参数解析
        Map argsValueMap = new HashMap();
        if (!batchFlag) {
            Map argsMaps = beanToMap(object);
            for (Object obj : argsMaps.keySet()) {
                if (obj.equals(primarykeyName)) {
                    argsValueMap.put("obj", argsMaps.get(obj));
                }
            }
            resultList.add(argsValueMap);
            return resultList;
        }

        //批量解析
        Map<String, Object> parameterMap = (Map) object;
        for (Map.Entry<String, Object> entery : parameterMap.entrySet()) {
            String key = entery.getKey();
            if (key.indexOf("param") < 0) {
                List<Object> objList = (List) entery.getValue();
                if (objList != null) {
                    for (Object obj : objList) {
                        Map<String, Object> listChild = new HashMap<>();
                        Class<?> clz = obj.getClass();
                        Field[] declaredFields = clz.getDeclaredFields();
                        for (int i = 0; i < declaredFields.length; i++) {
                            Field field = declaredFields[i];
                            field.setAccessible(true);
                            try {
                                //Object value = field.get(obj);
                                if (StringUtils.equals(primarykeyName, field.getName())) {
                                    Object value = field.get(obj);
                                    listChild.put(field.getName(), value);
                                }
                            } catch (IllegalAccessException e) {
                                e.printStackTrace();
                            }
                        }
                        resultList.add(listChild);
                    }
                }
            }
        }
        return resultList;
    }


    /**
     * bean转map
     *
     * @param bean
     * @return map
     */
    private static <T> Map<String, Object> beanToMap(T bean) {

        Map<String, Object> map = new HashMap<>();
        if (bean != null) {
            BeanMap beanMap = BeanMap.create(bean);
            for (Object key : beanMap.keySet()) {
                map.put(String.valueOf(key), beanMap.get(key));
            }
        }
        return map;
    }

    /**
     * @param jdbcTemplate    操作类型
     * @param tableName       表名
     * @param primarykeyName  主键名称
     * @param primarykeyValue 主键值
     * @param resultList      查询结果集
     * @return
     * @desc 操作前获取数据(修改或者删除的时候需要查询操作前的数据)
     */
    private static void queryData(JdbcTemplate jdbcTemplate, String tableName, String primarykeyName, Object primarykeyValue, List resultList) {

        if (StringUtils.isNotEmpty(tableName) && StringUtils.isNotEmpty(primarykeyName) && primarykeyValue != null) {
            StringBuffer sb = new StringBuffer();
            sb.append(" select * from ");
            sb.append(tableName);
            sb.append(" where ");
            sb.append(primarykeyName);
            sb.append(" = ? ");
            String sql = sb.toString();
            List<Map<String, Object>> resultMaps = jdbcTemplate.queryForList(sql, primarykeyValue);
            resultList.add(resultMaps);
        }
    }
}

4.SpringContextUtil工具类


import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Objects;

/**
 * @author liuminglin
 * @date 2021/6/22 10:48
 * @description: TODO
 */

@Component
public class SpringContextUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    /**
     * 实现ApplicationContextAware接口的回调方法,设置上下文环境
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        SpringContextUtil.applicationContext = applicationContext;
    }

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    /**
     * 获取对象
     *
     * @return Object 一个以所给名字注册的bean的实例 (service注解方式,自动生成以首字母小写的类名为bean name)
     */
    public static Object getBean(String name) {
        return applicationContext.getBean(name);
    }

    /**
     * 获取对象通过Class
     *
     * @param cls
     * @return Object
     * @throws BeansException
     */
    public static <C> Object getBean(Class<C> cls) {
        return applicationContext.getBean(cls);
    }

    //获取HttpServletRequest请求
    public static HttpServletRequest getHttpServletRequest() {
        return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
    }

}

5.RestResponse工具类(REST接口统一返回数据工具类封装)


import java.io.Serializable;

/**
 * REST接口统一返回数据工具类封装RestResponse
 */
public class RestResponse<T> implements Serializable {

    private static final long serialVersionUID = 3728877563912075885L;

    private int code;
    private String msg;
    private T data;


    public int getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }


    public T getData() {
        return data;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public void setData(T data) {
        this.data = data;
    }


    public RestResponse() {

    }

    public RestResponse(int code, String message, T data) {
        this.code = code;
        this.setMsg(message);
        this.data = data;
    }

    public RestResponse(int code, T data) {
        this.code = code;
        this.data = data;
    }

    public RestResponse(int code, String message) {
        this.code = code;
        this.setMsg(message);
    }

    /**
     * 成功时-返回data
     *
     * @param <T>
     * @return
     */
    public static <T> RestResponse<T> success(T data) {
        return new RestResponse<T>(200, null, data);
    }

    /**
     * 成功-不返回data
     *
     * @param <T>
     * @return
     */
    public static <T> RestResponse<T> success(String msg) {
        return new RestResponse<T>(200, msg);
    }

    /**
     * 成功-返回data+msg
     *
     * @param <T>
     * @return
     */
    public static <T> RestResponse<T> success(String msg, T data) {
        return new RestResponse<T>(200, msg, data);
    }

    /**
     * 失败
     *
     * @param <T>
     * @return
     */
    public static <T> RestResponse<T> fail(String msg) {
        return new RestResponse<T>(500, msg, null);
    }

    /**
     * 失败-code
     *
     * @param <T>
     * @return
     */
    public static <T> RestResponse<T> fail(int code, String msg) {
        return new RestResponse<T>(code, msg, null);
    }


    @Override
    public String toString() {
        return "RestResponse{" + "code=" + code + ", msg='" + msg + '\'' + ", data=" + data + '}';
    }


}

6.Controller调用

import com.example.recordlog.aspect.OperateLog;
import com.example.recordlog.bean.Goods;
import com.example.recordlog.bean.UserInfo;
import com.example.recordlog.constant.OperateTypeConstant;
import com.example.recordlog.mapper.GoodsMapper;
import com.example.recordlog.service.UserInfoService;
import com.example.recordlog.tools.RestResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/api")
public class OperateController {

    @Autowired
    private UserInfoService userInfoService;


    @RequestMapping(value = "/updateInfo", produces = "application/json;charset=utf-8", method = {RequestMethod.POST})
    public RestResponse updateInfo(@RequestBody UserInfo info) {
       int count = userInfoService.updateByPrimaryKeySelective(info);
        RestResponse restResponse = RestResponse.success(count);
        return restResponse;
    }


@RequestMapping(value = "/batchUpdateInfo", produces = "application/json;charset=utf-8", method = {RequestMethod.POST})
    public RestResponse batchUpdateInfo(@RequestBody List<UserInfo> userInfoList) {
        int count = userInfoService.batchUpdateInfo(userInfoList);
        RestResponse restResponse = RestResponse.success(count);
        return restResponse;
    }


    @RequestMapping(value = "/removeInfo", produces = "application/json;charset=utf-8", method = {RequestMethod.POST})
    public RestResponse removeInfo(@RequestBody Long id) {
        RestResponse restResponse = RestResponse.success(userInfoService.deleteByPrimaryKey(id));
        return restResponse;
    }

  • 8
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值